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 "Vehicle.h"
11 
12 #include "../Context.h"
13 #include "../Editor.h"
14 #include "../Game.h"
15 #include "../OpenRCT2.h"
16 #include "../actions/RideSetStatusAction.h"
17 #include "../audio/AudioMixer.h"
18 #include "../audio/audio.h"
19 #include "../config/Config.h"
20 #include "../core/Memory.hpp"
21 #include "../interface/Viewport.h"
22 #include "../localisation/Localisation.h"
23 #include "../management/NewsItem.h"
24 #include "../platform/platform.h"
25 #include "../rct12/RCT12.h"
26 #include "../scenario/Scenario.h"
27 #include "../scripting/HookEngine.h"
28 #include "../scripting/ScriptEngine.h"
29 #include "../util/Util.h"
30 #include "../windows/Intent.h"
31 #include "../world/Map.h"
32 #include "../world/MapAnimation.h"
33 #include "../world/Park.h"
34 #include "../world/Particle.h"
35 #include "../world/Scenery.h"
36 #include "../world/SmallScenery.h"
37 #include "../world/Sprite.h"
38 #include "../world/Surface.h"
39 #include "../world/Wall.h"
40 #include "CableLift.h"
41 #include "Ride.h"
42 #include "RideData.h"
43 #include "Station.h"
44 #include "Track.h"
45 #include "TrackData.h"
46 #include "TrainManager.h"
47 #include "VehicleData.h"
48 #include "VehicleSubpositionData.h"
49 
50 #include <algorithm>
51 #include <iterator>
52 
53 using namespace OpenRCT2::TrackMetaData;
54 static bool vehicle_boat_is_location_accessible(const CoordsXYZ& location);
55 
56 constexpr int16_t VEHICLE_MAX_SPIN_SPEED = 1536;
57 constexpr int16_t VEHICLE_MIN_SPIN_SPEED = -VEHICLE_MAX_SPIN_SPEED;
58 constexpr int16_t VEHICLE_MAX_SPIN_SPEED_FOR_STOPPING = 700;
59 constexpr int16_t VEHICLE_MAX_SPIN_SPEED_WATER_RIDE = 512;
60 constexpr int16_t VEHICLE_MIN_SPIN_SPEED_WATER_RIDE = -VEHICLE_MAX_SPIN_SPEED_WATER_RIDE;
61 constexpr int16_t VEHICLE_STOPPING_SPIN_SPEED = 600;
62 
63 Vehicle* gCurrentVehicle;
64 
65 static uint8_t _vehicleBreakdown;
66 StationIndex _vehicleStationIndex;
67 uint32_t _vehicleMotionTrackFlags;
68 int32_t _vehicleVelocityF64E08;
69 int32_t _vehicleVelocityF64E0C;
70 int32_t _vehicleUnkF64E10;
71 uint8_t _vehicleF64E2C;
72 Vehicle* _vehicleFrontVehicle;
73 CoordsXYZ unk_F64E20;
74 
75 static constexpr const OpenRCT2::Audio::SoundId byte_9A3A14[] = {
76     OpenRCT2::Audio::SoundId::Scream8,
77     OpenRCT2::Audio::SoundId::Scream1,
78 };
79 static constexpr const OpenRCT2::Audio::SoundId byte_9A3A16[] = {
80     OpenRCT2::Audio::SoundId::Scream1,
81     OpenRCT2::Audio::SoundId::Scream6,
82 };
83 static constexpr const OpenRCT2::Audio::SoundId byte_9A3A18[] = {
84     OpenRCT2::Audio::SoundId::Scream3, OpenRCT2::Audio::SoundId::Scream1, OpenRCT2::Audio::SoundId::Scream5,
85     OpenRCT2::Audio::SoundId::Scream6, OpenRCT2::Audio::SoundId::Scream7, OpenRCT2::Audio::SoundId::Scream2,
86     OpenRCT2::Audio::SoundId::Scream4,
87 };
88 
89 static constexpr const uint8_t _soundParams[OpenRCT2::Audio::RCT2SoundCount][2] = {
90     { 1, 0 }, // LiftClassic
91     { 1, 0 }, // TrackFrictionClassicWood
92     { 1, 0 }, // FrictionClassic
93     { 0, 1 }, // Scream1
94     { 0, 0 }, // Click1
95     { 0, 0 }, // Click2
96     { 0, 0 }, // PlaceItem
97     { 0, 1 }, // Scream2
98     { 0, 1 }, // Scream3
99     { 0, 1 }, // Scream4
100     { 0, 1 }, // Scream5
101     { 0, 1 }, // Scream6
102     { 1, 0 }, // LiftFrictionWheels
103     { 0, 0 }, // Purchase
104     { 0, 0 }, // Crash
105     { 0, 0 }, // LayingOutWater
106     { 0, 0 }, // Water1
107     { 0, 0 }, // Water2
108     { 0, 1 }, // TrainWhistle
109     { 0, 1 }, // TrainDeparting
110     { 0, 0 }, // WaterSplash
111     { 1, 0 }, // GoKartEngine
112     { 0, 0 }, // RideLaunch1
113     { 0, 0 }, // RideLaunch2
114     { 0, 0 }, // Cough1
115     { 0, 0 }, // Cough2
116     { 0, 0 }, // Cough3
117     { 0, 0 }, // Cough4
118     { 1, 0 }, // Rain
119     { 0, 0 }, // Thunder1
120     { 0, 0 }, // Thunder2
121     { 1, 0 }, // TrackFrictionTrain
122     { 1, 0 }, // TrackFrictionWater
123     { 0, 0 }, // BalloonPop
124     { 0, 0 }, // MechanicFix
125     { 0, 1 }, // Scream7
126     { 0, 0 }, // ToiletFlush
127     { 0, 0 }, // Click3
128     { 0, 0 }, // Quack
129     { 0, 0 }, // NewsItem
130     { 0, 0 }, // WindowOpen
131     { 0, 0 }, // Laugh1
132     { 0, 0 }, // Laugh2
133     { 0, 0 }, // Laugh3
134     { 0, 0 }, // Applause
135     { 0, 0 }, // HauntedHouseScare
136     { 0, 0 }, // HauntedHouseScream1
137     { 0, 0 }, // HauntedHouseScream2
138     { 0, 0 }, // BlockBrakeClose
139     { 0, 0 }, // BlockBrakeRelease
140     { 0, 0 }, // Error
141     { 0, 0 }, // BrakeRelease
142     { 1, 0 }, // LiftArrow
143     { 1, 0 }, // LiftWood
144     { 1, 0 }, // TrackFrictionWood
145     { 1, 0 }, // LiftWildMouse
146     { 1, 0 }, // LiftBM
147     { 1, 2 }, // TrackFrictionBM
148     { 0, 1 }, // Scream8
149     { 0, 1 }, // Tram
150     { 0, 0 }, // DoorOpen
151     { 0, 0 }, // DoorClose
152     { 0, 0 }, // Portcullis
153 };
154 
155 // clang-format off
156 static constexpr const uint8_t SpaceRingsTimeToSpriteMap[] =
157 {
158     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
159     1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4,
160     4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8,
161     8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13,
162     13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18,
163     19, 19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 22, 23, 23, 23, 0,
164     0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5,
165     5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
166     11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16,
167     16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21,
168     21, 22, 22, 22, 23, 23, 23, 0, 0, 0, 1, 1, 1, 2, 2, 2,
169     3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8,
170     8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13,
171     13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18,
172     19, 19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 22, 23, 23, 23, 0,
173     0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3,
174     3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6,
175     6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
176     7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5,
177     5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2,
178     2, 1, 1, 1, 1, 0, 0, 0, 0, 23, 23, 23, 22, 22, 22, 21,
179     21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16,
180     16, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11,
181     10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5,
182     5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0,
183     0, 23, 23, 23, 22, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19,
184     18, 18, 18, 17, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 13,
185     13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8,
186     8, 7, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3,
187     2, 2, 2, 1, 1, 1, 0, 0, 0, 23, 23, 23, 22, 22, 22, 21,
188     21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16,
189     16, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11,
190     10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6,
191     6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
192     3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0,
193     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24,
194     24, 24, 24, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 27,
195     27, 27, 27, 27, 28, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30,
196     30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35,
197     36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 41,
198     41, 41, 42, 42, 42, 43, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46,
199     46, 47, 47, 47, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 51, 51,
200     52, 52, 52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 57,
201     57, 57, 58, 58, 58, 59, 59, 59, 60, 60, 60, 61, 61, 61, 62, 62,
202     62, 63, 63, 63, 64, 64, 64, 65, 65, 65, 66, 66, 66, 67, 67, 67,
203     68, 68, 68, 69, 69, 69, 70, 70, 70, 71, 71, 71, 72, 72, 72, 73,
204     73, 73, 74, 74, 74, 75, 75, 75, 76, 76, 76, 77, 77, 77, 78, 78,
205     78, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82,
206     82, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85,
207     85, 85, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87,
208     87, 87, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 86, 86, 85,
209     85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83,
210     82, 82, 82, 82, 82, 81, 81, 81, 81, 80, 80, 80, 80, 79, 79, 79,
211     78, 78, 78, 77, 77, 77, 76, 76, 76, 75, 75, 75, 74, 74, 74, 73,
212     73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 69, 69, 69, 68, 68,
213     68, 67, 67, 67, 66, 66, 66, 65, 65, 65, 64, 64, 64, 63, 63, 63,
214     62, 62, 62, 61, 61, 61, 60, 60, 60, 59, 59, 59, 58, 58, 58, 57,
215     57, 57, 56, 56, 56, 55, 55, 55, 54, 54, 54, 53, 53, 53, 52, 52,
216     52, 51, 51, 51, 50, 50, 50, 49, 49, 49, 48, 48, 48, 47, 47, 47,
217     46, 46, 46, 45, 45, 45, 44, 44, 44, 43, 43, 43, 42, 42, 42, 41,
218     41, 41, 40, 40, 40, 39, 39, 39, 38, 38, 38, 37, 37, 37, 36, 36,
219     36, 35, 35, 35, 34, 34, 34, 33, 33, 33, 32, 32, 32, 31, 31, 31,
220     30, 30, 30, 30, 29, 29, 29, 29, 28, 28, 28, 28, 28, 27, 27, 27,
221     27, 27, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 24, 24,
222     24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
223     0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3,
224     3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6,
225     6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11,
226     11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16,
227     16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21, 21,
228     22, 22, 22, 23, 23, 23, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3,
229     3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8,
230     8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13,
231     14, 14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19,
232     19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 22, 23, 23, 23, 0, 0,
233     0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
234     6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11,
235     11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16,
236     16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21, 21,
237     22, 22, 22, 23, 23, 23, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2,
238     2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5,
239     5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7,
240     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6,
241     6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 3, 3,
242     3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0,
243     23, 23, 23, 22, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18,
244     18, 18, 17, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13,
245     13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8, 8,
246     7, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2,
247     2, 2, 1, 1, 1, 0, 0, 0, 23, 23, 23, 22, 22, 22, 21, 21,
248     21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16, 16,
249     15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10,
250     10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5,
251     5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0,
252     23, 23, 23, 22, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18,
253     18, 18, 17, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13,
254     13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8, 8,
255     7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4,
256     4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1,
257     1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
258     0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25,
259     26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28,
260     29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33,
261     33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38,
262     39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, 43, 43, 43, 44,
263     44, 44, 45, 45, 45, 46, 46, 46, 47, 47, 47, 48, 48, 48, 49, 49,
264     49, 50, 50, 50, 51, 51, 51, 52, 52, 52, 53, 53, 53, 54, 54, 54,
265     55, 55, 55, 56, 56, 56, 57, 57, 57, 58, 58, 58, 59, 59, 59, 60,
266     60, 60, 61, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 65, 65,
267     65, 66, 66, 66, 67, 67, 67, 68, 68, 68, 69, 69, 69, 70, 70, 70,
268     71, 71, 71, 72, 72, 72, 73, 73, 73, 74, 74, 74, 75, 75, 75, 76,
269     76, 76, 77, 77, 77, 78, 78, 78, 79, 79, 79, 80, 80, 80, 80, 81,
270     81, 81, 81, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 84, 84, 84,
271     84, 84, 84, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 86,
272     86, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 86,
273     86, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 84, 84, 84, 84,
274     84, 84, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 81, 81, 81, 81,
275     80, 80, 80, 80, 79, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 76,
276     75, 75, 75, 74, 74, 74, 73, 73, 73, 72, 72, 72, 71, 71, 71, 70,
277     70, 70, 69, 69, 69, 68, 68, 68, 67, 67, 67, 66, 66, 66, 65, 65,
278     65, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 61, 60, 60, 60,
279     59, 59, 59, 58, 58, 58, 57, 57, 57, 56, 56, 56, 55, 55, 55, 54,
280     54, 54, 53, 53, 53, 52, 52, 52, 51, 51, 51, 50, 50, 50, 49, 49,
281     49, 48, 48, 48, 47, 47, 47, 46, 46, 46, 45, 45, 45, 44, 44, 44,
282     43, 43, 43, 42, 42, 42, 41, 41, 41, 40, 40, 40, 39, 39, 39, 38,
283     38, 38, 37, 37, 37, 36, 36, 36, 35, 35, 35, 34, 34, 34, 33, 33,
284     33, 32, 32, 32, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 28,
285     28, 28, 28, 28, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 25,
286     25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24, 24, 24, 0,
287     255,
288 };
289 // clang-format on
290 
291 static constexpr const int8_t SwingingTimeToSpriteMap_0[] = {
292     0,  0,  0,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  3,  3,  3,    3,  3,
293     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  2,    2,  2,
294     2,  2,  2,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  -1, -1, -1, -1, -1, -1, -1, -2, -2, -2,   -2, -2,
295     -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,   -3, -3,
296     -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0,  0,  -128,
297 };
298 static constexpr const int8_t SwingingTimeToSpriteMap_1[] = {
299     0,  0,  1,  1,  1,  1,  2,  2,  2,  2,  2,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  4,  4,  4,  4,  5,    5,  5,
300     5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  4,  4,  4,  4,  4,  4,  4,  4,  4,  3,    3,  3,
301     3,  3,  3,  2,  2,  2,  2,  2,  1,  1,  1,  1,  0,  0,  0,  -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3,   -3, -3,
302     -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5,   -5, -5,
303     -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, 0,  -128,
304 };
305 static constexpr const int8_t SwingingTimeToSpriteMap_2[] = {
306     0,  0,  1,  1,  1,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,  5,  5,  5,    6,  6,  6,
307     6,  6,  6,  6,  6,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  6,  6,  6,    6,  6,  6,
308     6,  6,  5,  5,  5,  5,  5,  5,  4,  4,  4,  4,  4,  3,  3,  3,  3,  2,  2,  2,  1,  1,  1,    0,  0,  0,
309     -1, -1, -1, -2, -2, -2, -3, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -6, -6,   -6, -6, -6,
310     -6, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6,   -6, -6, -6,
311     -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -2, -2, -2, -1, -1, -1, 0,  -128,
312 };
313 static constexpr const int8_t SwingingTimeToSpriteMap_3[] = {
314     0,  1,  1,  2,  2,  3,  3,  4,  4,  4,  5,  5,  5,  5,  6,  6,  6,  6,  6,  7,  7,  7,  7,  7,    7,  8,  8,
315     8,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  8,  8,  8,  8,  8,    8,  8,  8,
316     7,  7,  7,  7,  7,  7,  6,  6,  6,  6,  6,  5,  5,  5,  5,  4,  4,  4,  3,  3,  2,  2,  1,  1,    0,  0,  -1,
317     -1, -2, -2, -3, -3, -4, -4, -4, -5, -5, -5, -5, -6, -6, -6, -6, -6, -7, -7, -7, -7, -7, -7, -8,   -8, -8, -8,
318     -8, -8, -8, -8, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -8,   -8, -7, -7,
319     -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -3, -3, -2, -2, -1, -1, 0,  -128,
320 };
321 static constexpr const int8_t SwingingTimeToSpriteMap_4[] = {
322     0,  0,  0,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  3,  3,  3,  3,  3,  4,  4,  4,    4,  4,  5,  5,  5,  5,  5,
323     5,  5,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  7,  7,  7,  7,  7,  7,  7,    7,  7,  7,  7,  7,  7,  7,
324     7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,    6,  6,  5,  5,  5,  5,  5,
325     5,  5,  4,  4,  4,  4,  4,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  1,  1,  1,  1,    1,  0,  0,  0,  0,  0,  -1,
326     -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5,   -5, -5, -5, -5, -5, -6, -6,
327     -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7,   -7, -7, -7, -7, -7, -7, -7,
328     -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5,   -5, -5, -5, -5, -5, -4, -4,
329     -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0,  0,  -128,
330 };
331 static constexpr const int8_t SwingingTimeToSpriteMap_5[] = {
332     0,   0,   1,   1,   1,   1,   2,   2,   2,   2,   3,   3,   3,   3,   4,   4,   4,   4,   5,   5,   5,    5,   6,   6,
333     6,   6,   7,   7,   7,   7,   8,   8,   8,   8,   9,   9,   9,   9,   10,  10,  10,  10,  11,  11,  11,   11,  12,  12,
334     12,  12,  13,  13,  13,  13,  13,  13,  14,  14,  14,  14,  14,  14,  14,  14,  14,  14,  15,  15,  15,   15,  15,  15,
335     15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  14,  14,  14,  14,  14,   14,  14,  14,
336     14,  14,  13,  13,  13,  13,  13,  13,  12,  12,  12,  12,  11,  11,  11,  11,  10,  10,  10,  10,  9,    9,   9,   9,
337     8,   8,   8,   8,   7,   7,   7,   7,   6,   6,   6,   6,   5,   5,   5,   5,   4,   4,   4,   4,   3,    3,   3,   3,
338     2,   2,   2,   2,   1,   1,   1,   1,   0,   0,   0,   0,   -1,  -1,  -1,  -1,  -2,  -2,  -2,  -2,  -3,   -3,  -3,  -3,
339     -4,  -4,  -4,  -4,  -5,  -5,  -5,  -5,  -6,  -6,  -6,  -6,  -7,  -7,  -7,  -7,  -8,  -8,  -8,  -8,  -9,   -9,  -9,  -9,
340     -10, -10, -10, -10, -11, -11, -11, -11, -12, -12, -12, -12, -13, -13, -13, -13, -13, -13, -14, -14, -14,  -14, -14, -14,
341     -14, -14, -14, -14, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15,  -15, -15, -15,
342     -15, -15, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -13, -12, -12, -12,  -12, -11, -11,
343     -11, -11, -10, -10, -10, -10, -9,  -9,  -9,  -9,  -8,  -8,  -8,  -8,  -7,  -7,  -7,  -7,  -6,  -6,  -6,   -6,  -5,  -5,
344     -5,  -5,  -4,  -4,  -4,  -4,  -3,  -3,  -3,  -3,  -2,  -2,  -2,  -2,  -1,  -1,  -1,  -1,  0,   0,   -128,
345 };
346 static constexpr const int8_t SwingingTimeToSpriteMap_6[] = {
347     0,   1,   1,   1,   2,   2,   2,   3,   3,   3,   4,   4,   4,   5,   5,   5,   6,   6,   6,   7,    7,   7,   8,
348     8,   8,   9,   9,   9,   10,  10,  10,  11,  11,  11,  12,  12,  12,  13,  13,  13,  14,  14,  14,   15,  15,  15,
349     16,  16,  16,  17,  17,  17,  18,  18,  18,  19,  19,  19,  20,  20,  20,  21,  21,  21,  22,  22,   22,  23,  23,
350     23,  23,  23,  24,  24,  24,  24,  24,  24,  24,  24,  24,  25,  25,  25,  25,  25,  25,  25,  25,   25,  25,  25,
351     25,  25,  25,  25,  25,  25,  25,  25,  24,  24,  24,  24,  24,  24,  24,  24,  24,  23,  23,  23,   23,  23,  22,
352     22,  22,  21,  21,  21,  20,  20,  20,  19,  19,  19,  18,  18,  18,  17,  17,  17,  16,  16,  16,   15,  15,  15,
353     14,  14,  14,  13,  13,  13,  12,  12,  12,  11,  11,  11,  10,  10,  10,  9,   9,   9,   8,   8,    8,   7,   7,
354     7,   6,   6,   6,   5,   5,   5,   4,   4,   4,   3,   3,   3,   2,   2,   2,   1,   1,   1,   0,    0,   0,   -1,
355     -1,  -1,  -2,  -2,  -2,  -3,  -3,  -3,  -4,  -4,  -4,  -5,  -5,  -5,  -6,  -6,  -6,  -7,  -7,  -7,   -8,  -8,  -8,
356     -9,  -9,  -9,  -10, -10, -10, -11, -11, -11, -12, -12, -12, -13, -13, -13, -14, -14, -14, -15, -15,  -15, -16, -16,
357     -16, -17, -17, -17, -18, -18, -18, -19, -19, -19, -20, -20, -20, -21, -21, -21, -22, -22, -22, -23,  -23, -23, -23,
358     -23, -24, -24, -24, -24, -24, -24, -24, -24, -24, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25,  -25, -25, -25,
359     -25, -25, -25, -25, -25, -25, -24, -24, -24, -24, -24, -24, -24, -24, -24, -23, -23, -23, -23, -23,  -22, -22, -22,
360     -21, -21, -21, -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15,  -15, -14, -14,
361     -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9,  -9,  -9,  -8,  -8,  -8,  -7,   -7,  -7,  -6,
362     -6,  -6,  -5,  -5,  -5,  -4,  -4,  -4,  -3,  -3,  -3,  -2,  -2,  -2,  -1,  -1,  -1,  0,   0,   -128,
363 };
364 static constexpr const int8_t SwingingTimeToSpriteMap_7[] = {
365     0,   1,   1,   1,   2,   2,   2,   3,   3,   3,   4,   4,   4,   5,   5,   5,   6,   6,   6,    7,   7,   7,
366     8,   8,   8,   9,   9,   9,   10,  10,  10,  11,  11,  11,  12,  12,  12,  13,  13,  13,  14,   14,  14,  15,
367     15,  15,  16,  16,  16,  17,  17,  17,  18,  18,  18,  19,  19,  19,  20,  20,  20,  21,  21,   21,  22,  22,
368     22,  23,  23,  23,  24,  24,  24,  25,  25,  25,  26,  26,  26,  27,  27,  27,  28,  28,  28,   29,  29,  29,
369     30,  30,  30,  31,  31,  31,  32,  32,  32,  33,  33,  33,  34,  34,  34,  35,  35,  35,  36,   36,  36,  -35,
370     -35, -35, -34, -34, -34, -33, -33, -33, -32, -32, -32, -31, -31, -31, -30, -30, -30, -29, -29,  -29, -28, -28,
371     -28, -27, -27, -27, -26, -26, -26, -25, -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -22,  -21, -21, -21,
372     -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14,  -14, -14, -13,
373     -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9,  -9,  -9,  -8,  -8,  -8,  -7,  -7,   -7,  -6,  -6,
374     -6,  -5,  -5,  -5,  -4,  -4,  -4,  -3,  -3,  -3,  -2,  -2,  -2,  -1,  -1,  -1,  0,   0,   -128,
375 };
376 static constexpr const int8_t SwingingTimeToSpriteMap_8[] = {
377     0,  0,  0,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  3,  3,  3,  3,  3,  4,  4,  4,    4,  4,  5,  5,  5,  5,  5,
378     5,  5,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  7,  7,  7,  7,  7,  7,  7,    7,  7,  7,  7,  7,  7,  7,
379     7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,    6,  6,  5,  5,  5,  5,  5,
380     5,  5,  4,  4,  4,  4,  4,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  1,  1,  1,  1,    1,  0,  0,  0,  0,  0,  31,
381     31, 31, 31, 31, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 28, 28, 28, 28, 28, 27, 27,   27, 27, 27, 27, 27, 26, 26,
382     26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,   25, 25, 25, 25, 25, 25, 25,
383     25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27,   27, 27, 27, 27, 27, 28, 28,
384     28, 28, 28, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 0,  0,  -128,
385 };
386 static constexpr const int8_t SwingingTimeToSpriteMap_9[] = {
387     0,  0,  0,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  3,  3,  3,  3,  3,  4,  4,  4,    4,  4,  5,  5,  5,  5,  5,
388     5,  5,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  7,  7,  7,  7,  7,  7,  7,    7,  7,  7,  7,  7,  7,  7,
389     7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,    6,  6,  5,  5,  5,  5,  5,
390     5,  5,  4,  4,  4,  4,  4,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  1,  1,  1,  1,    1,  0,  0,  0,  0,  0,  31,
391     31, 31, 31, 31, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 28, 28, 28, 28, 28, 27, 27,   27, 27, 27, 27, 27, 26, 26,
392     26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,   25, 25, 25, 25, 25, 25, 25,
393     25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27,   27, 27, 27, 27, 27, 28, 28,
394     28, 28, 28, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 0,  0,  -128,
395 };
396 static constexpr const int8_t SwingingTimeToSpriteMap_10[] = {
397     0,  0,  1,  1,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,  5,  6,  6,  6,  6,  7,  7,    7,  7,
398     8,  8,  8,  8,  9,  9,  9,  9,  10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14,   14, 14,
399     15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,   16, 16,
400     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,   16, 16,
401     16, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 12, 12, 12,   12, 11,
402     11, 11, 11, 10, 10, 10, 10, 9,  9,  9,  9,  8,  8,  8,  8,  7,  7,  7,  7,  6,  6,  6,  6,  5,  5,  5,  5,  4,    4,  4,
403     4,  3,  3,  3,  3,  2,  2,  2,  2,  1,  1,  1,  1,  0,  0,  0,  0,  31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29,   29, 28,
404     28, 28, 28, 27, 27, 27, 27, 26, 26, 26, 26, 25, 25, 25, 25, 24, 24, 24, 24, 23, 23, 23, 23, 22, 22, 22, 22, 21,   21, 21,
405     21, 20, 20, 20, 20, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16,   16, 16,
406     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18,   18, 18,
407     18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25,   25, 25,
408     25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 0,  0,  -128,
409 };
410 static constexpr const int8_t SwingingTimeToSpriteMap_11[] = {
411     0,  0,  1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  5,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,    8,
412     8,  9,  9,  9,  9,  10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15,   15,
413     16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22,   23,
414     23, 23, 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 0,  -128,
415 };
416 
417 /** rct2: 0x0099F9D0 */
418 static constexpr const int8_t* SwingingTimeToSpriteMaps[] = {
419     SwingingTimeToSpriteMap_0, SwingingTimeToSpriteMap_1, SwingingTimeToSpriteMap_2,  SwingingTimeToSpriteMap_3,
420     SwingingTimeToSpriteMap_4, SwingingTimeToSpriteMap_5, SwingingTimeToSpriteMap_6,  SwingingTimeToSpriteMap_7,
421     SwingingTimeToSpriteMap_8, SwingingTimeToSpriteMap_9, SwingingTimeToSpriteMap_10, SwingingTimeToSpriteMap_11,
422 };
423 
424 struct unk_9a36c4
425 {
426     int16_t x;
427     int16_t y;
428     uint32_t distance;
429 };
430 
431 /** rct2: 0x009A36C4 */
432 static constexpr const unk_9a36c4 Unk9A36C4[] = {
433     { -1, 0, 8716 }, { -1, 0, 8716 },   { -1, 0, 8716 },  { -1, 1, 12327 },  { -1, 1, 12327 },  { -1, 1, 12327 },
434     { 0, 1, 8716 },  { -1, 1, 12327 },  { 0, 1, 8716 },   { 0, 1, 8716 },    { 0, 1, 8716 },    { 1, 1, 12327 },
435     { 1, 1, 12327 }, { 1, 1, 12327 },   { 1, 0, 8716 },   { 1, 1, 12327 },   { 1, 0, 8716 },    { 1, 0, 8716 },
436     { 1, 0, 8716 },  { 1, -1, 12327 },  { 1, -1, 12327 }, { 1, -1, 12327 },  { 0, -1, 8716 },   { 1, -1, 12327 },
437     { 0, -1, 8716 }, { 0, -1, 8716 },   { 0, -1, 8716 },  { -1, -1, 12327 }, { -1, -1, 12327 }, { -1, -1, 12327 },
438     { -1, 0, 8716 }, { -1, -1, 12327 },
439 };
440 
441 /** rct2: 0x009A37C4 */
442 static constexpr const CoordsXY SurroundingTiles[] = {
443     { 0, 0 },
444     { 0, +COORDS_XY_STEP },
445     { +COORDS_XY_STEP, 0 },
446     { 0, -COORDS_XY_STEP },
447     { 0, -COORDS_XY_STEP },
448     { -COORDS_XY_STEP, 0 },
449     { -COORDS_XY_STEP, 0 },
450     { 0, +COORDS_XY_STEP },
451     { 0, +COORDS_XY_STEP },
452 };
453 
454 /** rct2: 0x009A37E4 */
455 static constexpr const int32_t Unk9A37E4[] = {
456     2147483647,  2106585154,  1985590284,  1636362342,  1127484953,  2106585154,  1985590284,  1636362342,  1127484953,
457     58579923,    0,           -555809667,  -1073741824, -1518500249, -1859775391, -2074309916, -2147483647, 58579923,
458     0,           -555809667,  -1073741824, -1518500249, -1859775391, -2074309916, 1859775393,  1073741824,  0,
459     -1073741824, -1859775393, 1859775393,  1073741824,  0,           -1073741824, -1859775393, 1859775393,  1073741824,
460     0,           -1073741824, -1859775393, 1859775393,  1073741824,  0,           -1073741824, -1859775393, 2144540595,
461     2139311823,  2144540595,  2139311823,  2135719507,  2135719507,  2125953864,  2061796213,  1411702590,  2125953864,
462     2061796213,  1411702590,  1985590284,  1636362342,  1127484953,  2115506168,
463 };
464 
465 /** rct2: 0x009A38D4 */
466 static constexpr const int32_t Unk9A38D4[] = {
467     0,           417115092,   817995863,   1390684831,  1827693544,  -417115092,  -817995863,  -1390684831, -1827693544,
468     2066040965,  2147483647,  2074309916,  1859775393,  1518500249,  1073741824,  555809666,   0,           -2066040965,
469     -2147483647, -2074309916, -1859775393, -1518500249, -1073741824, -555809666,  1073741824,  1859775393,  2147483647,
470     1859775393,  1073741824,  -1073741824, -1859775393, -2147483647, -1859775393, -1073741824, 1073741824,  1859775393,
471     2147483647,  1859775393,  1073741824,  -1073741824, -1859775393, -2147483647, -1859775393, -1073741824, 112390610,
472     187165532,   -112390610,  -187165532,  224473165,   -224473165,  303325208,   600568389,   1618265062,  -303325208,
473     -600568389,  -1618265062, -817995863,  -1390684831, -1827693544, 369214930,
474 };
475 
476 /** rct2: 0x009A39C4 */
477 static constexpr const int32_t Unk9A39C4[] = {
478     2147483647, 2096579710, 1946281152, 2096579710,  1946281152,  1380375879, 555809667,  -372906620, -1231746017, -1859775391,
479     1380375879, 555809667,  -372906620, -1231746017, -1859775391, 0,          2096579710, 1946281152, 2096579710,  1946281152,
480 };
481 
482 static constexpr const CoordsXY AvoidCollisionMoveOffset[] = {
483     { -1, 0 },
484     { 0, 1 },
485     { 1, 0 },
486     { 0, -1 },
487 };
488 
489 static constexpr const OpenRCT2::Audio::SoundId DoorOpenSoundIds[] = {
490     OpenRCT2::Audio::SoundId::DoorOpen,
491     OpenRCT2::Audio::SoundId::Portcullis,
492 };
493 
494 static constexpr const OpenRCT2::Audio::SoundId DoorCloseSoundIds[] = {
495     OpenRCT2::Audio::SoundId::DoorClose,
496     OpenRCT2::Audio::SoundId::Portcullis,
497 };
498 
499 static const struct
500 {
501     int8_t x, y, z;
502 } SteamParticleOffsets[][16] = {
503     {
504         { -11, 0, 22 },
505         { -10, 4, 22 },
506         { -8, 8, 22 },
507         { -4, 10, 22 },
508         { 0, 11, 22 },
509         { 4, 10, 22 },
510         { 8, 8, 22 },
511         { 10, 4, 22 },
512         { 11, 0, 22 },
513         { 10, -4, 22 },
514         { 8, -8, 22 },
515         { 4, -10, 22 },
516         { 0, -11, 22 },
517         { -4, -10, 22 },
518         { -8, -8, 22 },
519         { -10, -4, 22 },
520     },
521     {
522         { -9, 0, 27 },
523         { -8, 4, 27 },
524         { -6, 6, 27 },
525         { -4, 8, 27 },
526         { 0, 9, 27 },
527         { 4, 8, 27 },
528         { 6, 6, 27 },
529         { 8, 4, 27 },
530         { 9, 0, 27 },
531         { 8, -4, 27 },
532         { 6, -6, 27 },
533         { 4, -8, 27 },
534         { 0, -9, 27 },
535         { -4, -8, 27 },
536         { -6, -6, 27 },
537         { -8, -4, 27 },
538     },
539     {
540         { -13, 0, 18 },
541         { -12, 4, 17 },
542         { -9, 9, 17 },
543         { -4, 8, 17 },
544         { 0, 13, 18 },
545         { 4, 8, 17 },
546         { 6, 6, 17 },
547         { 8, 4, 17 },
548         { 13, 0, 18 },
549         { 8, -4, 17 },
550         { 6, -6, 17 },
551         { 4, -8, 17 },
552         { 0, -13, 18 },
553         { -4, -8, 17 },
554         { -6, -6, 17 },
555         { -8, -4, 17 },
556     },
557 };
558 
Is() const559 template<> bool EntityBase::Is<Vehicle>() const
560 {
561     return Type == EntityType::Vehicle;
562 }
563 
564 #ifdef ENABLE_SCRIPTING
565 /**
566  * Fires the "vehicle.crash" api hook
567  * @param vehicleId Entity id of the vehicle that just crashed
568  * @param crashId What the vehicle crashed into. Should be either "another_vehicle", "land", or "water"
569  */
InvokeVehicleCrashHook(const uint16_t vehicleId,const std::string_view crashId)570 static void InvokeVehicleCrashHook(const uint16_t vehicleId, const std::string_view crashId)
571 {
572     auto& hookEngine = OpenRCT2::GetContext()->GetScriptEngine().GetHookEngine();
573     if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::VEHICLE_CRASH))
574     {
575         auto ctx = OpenRCT2::GetContext()->GetScriptEngine().GetContext();
576 
577         // Create event args object
578         auto obj = OpenRCT2::Scripting::DukObject(ctx);
579         obj.Set("id", vehicleId);
580         obj.Set("crashIntoType", crashId);
581 
582         // Call the subscriptions
583         auto e = obj.Take();
584         hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::VEHICLE_CRASH, e, true);
585     }
586 }
587 #endif
588 
vehicle_move_info_valid(VehicleTrackSubposition trackSubposition,track_type_t type,uint8_t direction,int32_t offset)589 static bool vehicle_move_info_valid(
590     VehicleTrackSubposition trackSubposition, track_type_t type, uint8_t direction, int32_t offset)
591 {
592     uint16_t typeAndDirection = (type << 2) | (direction & 3);
593 
594     if (trackSubposition >= VehicleTrackSubposition{ std::size(gTrackVehicleInfo) })
595     {
596         return false;
597     }
598     int32_t size = 0;
599     switch (trackSubposition)
600     {
601         case VehicleTrackSubposition::Default:
602             size = VehicleTrackSubpositionSizeDefault;
603             break;
604         case VehicleTrackSubposition::ChairliftGoingOut:
605             size = 692;
606             break;
607         case VehicleTrackSubposition::ChairliftGoingBack:
608         case VehicleTrackSubposition::ChairliftEndBullwheel:
609         case VehicleTrackSubposition::ChairliftStartBullwheel:
610             size = 404;
611             break;
612         case VehicleTrackSubposition::GoKartsLeftLane:
613         case VehicleTrackSubposition::GoKartsRightLane:
614         case VehicleTrackSubposition::GoKartsMovingToRightLane:
615         case VehicleTrackSubposition::GoKartsMovingToLeftLane:
616             size = 208;
617             break;
618         case VehicleTrackSubposition::MiniGolfPathA9: // VehicleTrackSubposition::MiniGolfStart9
619         case VehicleTrackSubposition::MiniGolfBallPathA10:
620         case VehicleTrackSubposition::MiniGolfPathB11:
621         case VehicleTrackSubposition::MiniGolfBallPathB12:
622         case VehicleTrackSubposition::MiniGolfPathC13:
623         case VehicleTrackSubposition::MiniGolfBallPathC14:
624             size = 824;
625             break;
626         case VehicleTrackSubposition::ReverserRCFrontBogie:
627         case VehicleTrackSubposition::ReverserRCRearBogie:
628             size = 868;
629             break;
630         default:
631             break;
632     }
633     if (typeAndDirection >= size)
634     {
635         return false;
636     }
637     if (offset >= gTrackVehicleInfo[static_cast<uint8_t>(trackSubposition)][typeAndDirection]->size)
638     {
639         return false;
640     }
641     return true;
642 }
643 
vehicle_get_move_info(VehicleTrackSubposition trackSubposition,track_type_t type,uint8_t direction,int32_t offset)644 static const rct_vehicle_info* vehicle_get_move_info(
645     VehicleTrackSubposition trackSubposition, track_type_t type, uint8_t direction, int32_t offset)
646 {
647     uint16_t typeAndDirection = (type << 2) | (direction & 3);
648 
649     if (!vehicle_move_info_valid(trackSubposition, type, direction, offset))
650     {
651         static constexpr const rct_vehicle_info zero = {};
652         return &zero;
653     }
654     return &gTrackVehicleInfo[static_cast<uint8_t>(trackSubposition)][typeAndDirection]->info[offset];
655 }
656 
GetMoveInfo() const657 const rct_vehicle_info* Vehicle::GetMoveInfo() const
658 {
659     return vehicle_get_move_info(TrackSubposition, GetTrackType(), GetTrackDirection(), track_progress);
660 }
661 
vehicle_get_move_info_size(VehicleTrackSubposition trackSubposition,track_type_t type,uint8_t direction)662 static uint16_t vehicle_get_move_info_size(VehicleTrackSubposition trackSubposition, track_type_t type, uint8_t direction)
663 {
664     uint16_t typeAndDirection = (type << 2) | (direction & 3);
665 
666     if (!vehicle_move_info_valid(trackSubposition, type, direction, 0))
667     {
668         return 0;
669     }
670     return gTrackVehicleInfo[static_cast<uint8_t>(trackSubposition)][typeAndDirection]->size;
671 }
672 
GetTrackProgress() const673 uint16_t Vehicle::GetTrackProgress() const
674 {
675     return vehicle_get_move_info_size(TrackSubposition, GetTrackType(), GetTrackDirection());
676 }
677 
ApplyMass(int16_t appliedMass)678 void Vehicle::ApplyMass(int16_t appliedMass)
679 {
680     mass = std::clamp<int32_t>(mass + appliedMass, 1, std::numeric_limits<decltype(mass)>::max());
681 }
682 
MoveRelativeDistance(int32_t distance)683 void Vehicle::MoveRelativeDistance(int32_t distance)
684 {
685     remaining_distance += distance;
686 
687     SetUpdateFlag(VEHICLE_UPDATE_FLAG_SINGLE_CAR_POSITION | VEHICLE_UPDATE_FLAG_COLLISION_DISABLED);
688     UpdateTrackMotion(nullptr);
689     ClearUpdateFlag(VEHICLE_UPDATE_FLAG_SINGLE_CAR_POSITION | VEHICLE_UPDATE_FLAG_COLLISION_DISABLED);
690 }
691 
try_get_vehicle(uint16_t spriteIndex)692 Vehicle* try_get_vehicle(uint16_t spriteIndex)
693 {
694     return TryGetEntity<Vehicle>(spriteIndex);
695 }
696 
697 namespace
698 {
699     template<typename T> class TrainIterator;
700     template<typename T> class Train
701     {
702     public:
Train(T * vehicle)703         explicit Train(T* vehicle)
704             : FirstCar(vehicle)
705         {
706             assert(FirstCar->IsHead());
707         }
708         int32_t Mass();
709 
710         friend class TrainIterator<T>;
711         using iterator = TrainIterator<T>;
begin()712         iterator begin()
713         {
714             return iterator{ FirstCar };
715         }
end()716         iterator end()
717         {
718             return iterator{};
719         }
720 
721     private:
722         T* FirstCar;
723     };
724     template<typename T> class TrainIterator
725     {
726     public:
727         using iterator = TrainIterator;
728         using iterator_category = std::forward_iterator_tag;
729         using value_type = T;
730         using pointer = T*;
731         using reference = T&;
732 
733         TrainIterator() = default;
TrainIterator(T * vehicle)734         explicit TrainIterator(T* vehicle)
735             : Current(vehicle)
736         {
737         }
operator *()738         reference operator*()
739         {
740             return *Current;
741         }
operator ++()742         iterator& operator++()
743         {
744             Current = GetEntity<Vehicle>(NextVehicleId);
745             if (Current != nullptr)
746             {
747                 NextVehicleId = Current->next_vehicle_on_train;
748             }
749             return *this;
750         }
operator ++(int)751         iterator operator++(int)
752         {
753             iterator temp = *this;
754             ++*this;
755             return temp;
756         }
operator !=(const iterator & other)757         bool operator!=(const iterator& other)
758         {
759             return Current != other.Current;
760         }
761 
762     private:
763         T* Current = nullptr;
764         uint16_t NextVehicleId = SPRITE_INDEX_NULL;
765     };
766 } // namespace
767 
Mass()768 template<typename T> int32_t Train<T>::Mass()
769 {
770     int32_t totalMass = 0;
771     for (const auto& vehicle : *this)
772     {
773         totalMass += vehicle.mass;
774     }
775 
776     return totalMass;
777 }
778 
SoundCanPlay() const779 bool Vehicle::SoundCanPlay() const
780 {
781     if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)
782         return false;
783 
784     if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) && gEditorStep != EditorStep::RollercoasterDesigner)
785         return false;
786 
787     if (sound1_id == OpenRCT2::Audio::SoundId::Null && sound2_id == OpenRCT2::Audio::SoundId::Null)
788         return false;
789 
790     if (x == LOCATION_NULL)
791         return false;
792 
793     if (g_music_tracking_viewport == nullptr)
794         return false;
795 
796     const auto quarter_w = g_music_tracking_viewport->view_width / 4;
797     const auto quarter_h = g_music_tracking_viewport->view_height / 4;
798 
799     auto left = g_music_tracking_viewport->viewPos.x;
800     auto bottom = g_music_tracking_viewport->viewPos.y;
801 
802     if (window_get_classification(gWindowAudioExclusive) == WC_MAIN_WINDOW)
803     {
804         left -= quarter_w;
805         bottom -= quarter_h;
806     }
807 
808     if (left >= SpriteRect.GetRight() || bottom >= SpriteRect.GetBottom())
809         return false;
810 
811     auto right = g_music_tracking_viewport->view_width + left;
812     auto top = g_music_tracking_viewport->view_height + bottom;
813 
814     if (window_get_classification(gWindowAudioExclusive) == WC_MAIN_WINDOW)
815     {
816         right += quarter_w + quarter_w;
817         top += quarter_h + quarter_h;
818     }
819 
820     if (right < SpriteRect.GetRight() || top < SpriteRect.GetTop())
821         return false;
822 
823     return true;
824 }
825 
826 /**
827  *
828  *  rct2: 0x006BC2F3
829  */
GetSoundPriority() const830 uint16_t Vehicle::GetSoundPriority() const
831 {
832     int32_t result = Train(this).Mass() + (std::abs(velocity) >> 13);
833 
834     for (const auto& vehicleSound : OpenRCT2::Audio::gVehicleSoundList)
835     {
836         if (vehicleSound.id == sprite_index)
837         {
838             // Vehicle sounds will get higher priority if they are already playing
839             return result + 300;
840         }
841     }
842 
843     return result;
844 }
845 
CreateSoundParam(uint16_t priority) const846 OpenRCT2::Audio::VehicleSoundParams Vehicle::CreateSoundParam(uint16_t priority) const
847 {
848     OpenRCT2::Audio::VehicleSoundParams param;
849     param.priority = priority;
850     int32_t panX = (SpriteRect.GetLeft() / 2) + (SpriteRect.GetRight() / 2) - g_music_tracking_viewport->viewPos.x;
851     panX = panX / g_music_tracking_viewport->zoom;
852     panX += g_music_tracking_viewport->pos.x;
853 
854     uint16_t screenWidth = context_get_width();
855     if (screenWidth < 64)
856     {
857         screenWidth = 64;
858     }
859     param.pan_x = ((((panX * 65536) / screenWidth) - 0x8000) >> 4);
860 
861     int32_t panY = (SpriteRect.GetTop() / 2) + (SpriteRect.GetBottom() / 2) - g_music_tracking_viewport->viewPos.y;
862     panY = panY / g_music_tracking_viewport->zoom;
863     panY += g_music_tracking_viewport->pos.y;
864 
865     uint16_t screenHeight = context_get_height();
866     if (screenHeight < 64)
867     {
868         screenHeight = 64;
869     }
870     param.pan_y = ((((panY * 65536) / screenHeight) - 0x8000) >> 4);
871 
872     int32_t frequency = std::abs(velocity);
873 
874     rct_ride_entry* rideType = GetRideEntry();
875     if (rideType != nullptr)
876     {
877         if (rideType->vehicles[vehicle_type].double_sound_frequency & 1)
878         {
879             frequency *= 2;
880         }
881     }
882 
883     // * 0.0105133...
884     frequency >>= 5; // /32
885     frequency *= 5512;
886     frequency >>= 14; // /16384
887 
888     frequency += 11025;
889     frequency += 16 * sound_vector_factor;
890     param.frequency = static_cast<uint16_t>(frequency);
891     param.id = sprite_index;
892     param.volume = 0;
893 
894     if (x != LOCATION_NULL)
895     {
896         auto surfaceElement = map_get_surface_element_at(CoordsXY{ x, y });
897 
898         // vehicle underground
899         if (surfaceElement != nullptr && surfaceElement->GetBaseZ() > z)
900         {
901             param.volume = 0x30;
902         }
903     }
904     return param;
905 }
906 
907 /**
908  *
909  *  rct2: 0x006BB9FF
910  */
UpdateSoundParams(std::vector<OpenRCT2::Audio::VehicleSoundParams> & vehicleSoundParamsList) const911 void Vehicle::UpdateSoundParams(std::vector<OpenRCT2::Audio::VehicleSoundParams>& vehicleSoundParamsList) const
912 {
913     if (!SoundCanPlay())
914         return;
915 
916     uint16_t soundPriority = GetSoundPriority();
917     // Find a sound param of lower priority to use
918     auto soundParamIter = std::find_if(
919         vehicleSoundParamsList.begin(), vehicleSoundParamsList.end(),
920         [soundPriority](const auto& param) { return soundPriority > param.priority; });
921 
922     if (soundParamIter == std::end(vehicleSoundParamsList))
923     {
924         if (vehicleSoundParamsList.size() < OpenRCT2::Audio::MaxVehicleSounds)
925         {
926             vehicleSoundParamsList.push_back(CreateSoundParam(soundPriority));
927         }
928     }
929     else
930     {
931         if (vehicleSoundParamsList.size() < OpenRCT2::Audio::MaxVehicleSounds)
932         {
933             // Shift all sound params down one if using a free space
934             vehicleSoundParamsList.insert(soundParamIter, CreateSoundParam(soundPriority));
935         }
936         else
937         {
938             *soundParamIter = CreateSoundParam(soundPriority);
939         }
940     }
941 }
942 
vehicle_sounds_update_window_setup()943 static void vehicle_sounds_update_window_setup()
944 {
945     g_music_tracking_viewport = nullptr;
946 
947     rct_window* window = window_get_listening();
948     if (window == nullptr)
949     {
950         return;
951     }
952 
953     rct_viewport* viewport = window_get_viewport(window);
954     if (viewport == nullptr)
955     {
956         return;
957     }
958 
959     g_music_tracking_viewport = viewport;
960     gWindowAudioExclusive = window;
961     if (viewport->zoom <= 0)
962         OpenRCT2::Audio::gVolumeAdjustZoom = 0;
963     else if (viewport->zoom == 1)
964         OpenRCT2::Audio::gVolumeAdjustZoom = 35;
965     else
966         OpenRCT2::Audio::gVolumeAdjustZoom = 70;
967 }
968 
vehicle_sounds_update_get_pan_volume(OpenRCT2::Audio::VehicleSoundParams * sound_params)969 static uint8_t vehicle_sounds_update_get_pan_volume(OpenRCT2::Audio::VehicleSoundParams* sound_params)
970 {
971     uint8_t vol1 = 0xFF;
972     uint8_t vol2 = 0xFF;
973 
974     int16_t pan_y = std::abs(sound_params->pan_y);
975     pan_y = std::min(static_cast<int16_t>(0xFFF), pan_y);
976     pan_y -= 0x800;
977     if (pan_y > 0)
978     {
979         pan_y = (0x400 - pan_y) / 4;
980         vol1 = LOBYTE(pan_y);
981         if (static_cast<int8_t>(HIBYTE(pan_y)) != 0)
982         {
983             vol1 = 0xFF;
984             if (static_cast<int8_t>(HIBYTE(pan_y)) < 0)
985             {
986                 vol1 = 0;
987             }
988         }
989     }
990 
991     int16_t pan_x = std::abs(sound_params->pan_x);
992     pan_x = std::min(static_cast<int16_t>(0xFFF), pan_x);
993     pan_x -= 0x800;
994 
995     if (pan_x > 0)
996     {
997         pan_x = (0x400 - pan_x) / 4;
998         vol2 = LOBYTE(pan_x);
999         if (static_cast<int8_t>(HIBYTE(pan_x)) != 0)
1000         {
1001             vol2 = 0xFF;
1002             if (static_cast<int8_t>(HIBYTE(pan_x)) < 0)
1003             {
1004                 vol2 = 0;
1005             }
1006         }
1007     }
1008 
1009     vol1 = std::min(vol1, vol2);
1010     return std::max(0, vol1 - OpenRCT2::Audio::gVolumeAdjustZoom);
1011 }
1012 
1013 /*  Returns the vehicle sound for a sound_param.
1014  *
1015  *  If already playing returns sound.
1016  *  If not playing allocates a sound slot to sound_param->id.
1017  *  If no free slots returns nullptr.
1018  */
vehicle_sounds_update_get_vehicle_sound(OpenRCT2::Audio::VehicleSoundParams * sound_params)1019 static OpenRCT2::Audio::VehicleSound* vehicle_sounds_update_get_vehicle_sound(OpenRCT2::Audio::VehicleSoundParams* sound_params)
1020 {
1021     // Search for already playing vehicle sound
1022     for (auto& vehicleSound : OpenRCT2::Audio::gVehicleSoundList)
1023     {
1024         if (vehicleSound.id == sound_params->id)
1025             return &vehicleSound;
1026     }
1027 
1028     // No sound already playing
1029     for (auto& vehicleSound : OpenRCT2::Audio::gVehicleSoundList)
1030     {
1031         // Use free slot
1032         if (vehicleSound.id == OpenRCT2::Audio::SoundIdNull)
1033         {
1034             vehicleSound.id = sound_params->id;
1035             vehicleSound.TrackSound.Id = OpenRCT2::Audio::SoundId::Null;
1036             vehicleSound.OtherSound.Id = OpenRCT2::Audio::SoundId::Null;
1037             vehicleSound.volume = 0x30;
1038             return &vehicleSound;
1039         }
1040     }
1041 
1042     return nullptr;
1043 }
1044 
1045 enum class SoundType
1046 {
1047     TrackNoises,
1048     OtherNoises, // e.g. Screams
1049 };
1050 
SoundFrequency(const OpenRCT2::Audio::SoundId id,uint16_t baseFrequency)1051 template<SoundType type> static uint16_t SoundFrequency(const OpenRCT2::Audio::SoundId id, uint16_t baseFrequency)
1052 {
1053     if constexpr (type == SoundType::TrackNoises)
1054     {
1055         if (_soundParams[static_cast<uint8_t>(id)][1] & 2)
1056         {
1057             return (baseFrequency / 2) + 4000;
1058         }
1059         return baseFrequency;
1060     }
1061     else
1062     {
1063         if (_soundParams[static_cast<uint8_t>(id)][1] & 1)
1064         {
1065             return 22050;
1066         }
1067         return std::min((baseFrequency * 2) - 3248, 25700);
1068     }
1069 }
1070 
ShouldUpdateChannelRate(const OpenRCT2::Audio::SoundId id)1071 template<SoundType type> static bool ShouldUpdateChannelRate(const OpenRCT2::Audio::SoundId id)
1072 {
1073     return type == SoundType::TrackNoises || !(_soundParams[static_cast<uint8_t>(id)][1] & 1);
1074 }
1075 
1076 template<SoundType type>
UpdateSound(const OpenRCT2::Audio::SoundId id,int32_t volume,OpenRCT2::Audio::VehicleSoundParams * sound_params,OpenRCT2::Audio::Sound & sound,uint8_t panVol)1077 static void UpdateSound(
1078     const OpenRCT2::Audio::SoundId id, int32_t volume, OpenRCT2::Audio::VehicleSoundParams* sound_params,
1079     OpenRCT2::Audio::Sound& sound, uint8_t panVol)
1080 {
1081     volume *= panVol;
1082     volume = volume / 8;
1083     volume = std::max(volume - 0x1FFF, -10000);
1084 
1085     if (id == OpenRCT2::Audio::SoundId::Null)
1086     {
1087         if (sound.Id != OpenRCT2::Audio::SoundId::Null)
1088         {
1089             sound.Id = OpenRCT2::Audio::SoundId::Null;
1090             Mixer_Stop_Channel(sound.Channel);
1091         }
1092         return;
1093     }
1094 
1095     if (sound.Id != OpenRCT2::Audio::SoundId::Null && id != sound.Id)
1096     {
1097         Mixer_Stop_Channel(sound.Channel);
1098     }
1099 
1100     if ((sound.Id == OpenRCT2::Audio::SoundId::Null) || (id != sound.Id))
1101     {
1102         sound.Id = id;
1103         sound.Pan = sound_params->pan_x;
1104         sound.Volume = volume;
1105         sound.Frequency = sound_params->frequency;
1106         uint16_t frequency = SoundFrequency<type>(id, sound_params->frequency);
1107         uint8_t looping = _soundParams[static_cast<uint8_t>(id)][0];
1108         int32_t pan = sound_params->pan_x;
1109         sound.Channel = Mixer_Play_Effect(
1110             id, looping ? MIXER_LOOP_INFINITE : MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(pan),
1111             DStoMixerRate(frequency), 0);
1112         return;
1113     }
1114     if (volume != sound.Volume)
1115     {
1116         sound.Volume = volume;
1117         Mixer_Channel_Volume(sound.Channel, DStoMixerVolume(volume));
1118     }
1119     if (sound_params->pan_x != sound.Pan)
1120     {
1121         sound.Pan = sound_params->pan_x;
1122         Mixer_Channel_Pan(sound.Channel, DStoMixerPan(sound_params->pan_x));
1123     }
1124     if (!(gCurrentTicks & 3) && sound_params->frequency != sound.Frequency)
1125     {
1126         sound.Frequency = sound_params->frequency;
1127         if (ShouldUpdateChannelRate<type>(id))
1128         {
1129             uint16_t frequency = SoundFrequency<type>(id, sound_params->frequency);
1130             Mixer_Channel_Rate(sound.Channel, DStoMixerRate(frequency));
1131         }
1132     }
1133 }
1134 
1135 /**
1136  *
1137  *  rct2: 0x006BBC6B
1138  */
vehicle_sounds_update()1139 void vehicle_sounds_update()
1140 {
1141     if (!OpenRCT2::Audio::IsAvailable())
1142         return;
1143 
1144     std::vector<OpenRCT2::Audio::VehicleSoundParams> vehicleSoundParamsList;
1145     vehicleSoundParamsList.reserve(OpenRCT2::Audio::MaxVehicleSounds);
1146 
1147     vehicle_sounds_update_window_setup();
1148 
1149     for (auto vehicle : TrainManager::View())
1150     {
1151         vehicle->UpdateSoundParams(vehicleSoundParamsList);
1152     }
1153 
1154     // Stop all playing sounds that no longer have priority to play after vehicle_update_sound_params
1155     for (auto& vehicle_sound : OpenRCT2::Audio::gVehicleSoundList)
1156     {
1157         if (vehicle_sound.id != OpenRCT2::Audio::SoundIdNull)
1158         {
1159             bool keepPlaying = false;
1160             for (auto vehicleSoundParams : vehicleSoundParamsList)
1161             {
1162                 if (vehicle_sound.id == vehicleSoundParams.id)
1163                 {
1164                     keepPlaying = true;
1165                     break;
1166                 }
1167             }
1168 
1169             if (keepPlaying)
1170                 continue;
1171 
1172             if (vehicle_sound.TrackSound.Id != OpenRCT2::Audio::SoundId::Null)
1173             {
1174                 Mixer_Stop_Channel(vehicle_sound.TrackSound.Channel);
1175             }
1176             if (vehicle_sound.OtherSound.Id != OpenRCT2::Audio::SoundId::Null)
1177             {
1178                 Mixer_Stop_Channel(vehicle_sound.OtherSound.Channel);
1179             }
1180             vehicle_sound.id = OpenRCT2::Audio::SoundIdNull;
1181         }
1182     }
1183 
1184     for (auto& vehicleSoundParams : vehicleSoundParamsList)
1185     {
1186         uint8_t panVol = vehicle_sounds_update_get_pan_volume(&vehicleSoundParams);
1187 
1188         auto* vehicleSound = vehicle_sounds_update_get_vehicle_sound(&vehicleSoundParams);
1189         // No free vehicle sound slots (RCT2 corrupts the pointer here)
1190         if (vehicleSound == nullptr)
1191             continue;
1192 
1193         // Move the Sound Volume towards the SoundsParam Volume
1194         int32_t tempvolume = vehicleSound->volume;
1195         if (tempvolume != vehicleSoundParams.volume)
1196         {
1197             if (tempvolume < vehicleSoundParams.volume)
1198             {
1199                 tempvolume += 4;
1200             }
1201             else
1202             {
1203                 tempvolume -= 4;
1204             }
1205         }
1206         vehicleSound->volume = tempvolume;
1207         panVol = std::max(0, panVol - tempvolume);
1208 
1209         Vehicle* vehicle = GetEntity<Vehicle>(vehicleSoundParams.id);
1210         if (vehicle != nullptr)
1211         {
1212             UpdateSound<SoundType::TrackNoises>(
1213                 vehicle->sound1_id, vehicle->sound1_volume, &vehicleSoundParams, vehicleSound->TrackSound, panVol);
1214             UpdateSound<SoundType::OtherNoises>(
1215                 vehicle->sound2_id, vehicle->sound2_volume, &vehicleSoundParams, vehicleSound->OtherSound, panVol);
1216         }
1217     }
1218 }
1219 
1220 /**
1221  *
1222  *  rct2: 0x006D4204
1223  */
vehicle_update_all()1224 void vehicle_update_all()
1225 {
1226     if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)
1227         return;
1228 
1229     if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) && gEditorStep != EditorStep::RollercoasterDesigner)
1230         return;
1231 
1232     for (auto vehicle : TrainManager::View())
1233     {
1234         vehicle->Update();
1235     }
1236 }
1237 
1238 /**
1239  *
1240  *  rct2: 0x006D6956
1241  * @returns true when all closed
1242  */
CloseRestraints()1243 bool Vehicle::CloseRestraints()
1244 {
1245     auto curRide = GetRide();
1246     if (curRide == nullptr)
1247         return true;
1248 
1249     bool restraintsClosed = true;
1250     for (Vehicle* vehicle = GetEntity<Vehicle>(sprite_index); vehicle != nullptr;
1251          vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
1252     {
1253         if (vehicle->HasUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_CAR) && vehicle->restraints_position != 0
1254             && (curRide->breakdown_reason_pending == BREAKDOWN_RESTRAINTS_STUCK_OPEN
1255                 || curRide->breakdown_reason_pending == BREAKDOWN_DOORS_STUCK_OPEN))
1256         {
1257             if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN))
1258             {
1259                 curRide->lifecycle_flags |= RIDE_LIFECYCLE_BROKEN_DOWN;
1260 
1261                 ride_breakdown_add_news_item(curRide);
1262 
1263                 curRide->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST
1264                     | RIDE_INVALIDATE_RIDE_MAINTENANCE;
1265 
1266                 curRide->mechanic_status = RIDE_MECHANIC_STATUS_CALLING;
1267 
1268                 Vehicle* broken_vehicle = GetEntity<Vehicle>(curRide->vehicles[curRide->broken_vehicle]);
1269                 if (broken_vehicle != nullptr)
1270                 {
1271                     curRide->inspection_station = broken_vehicle->current_station;
1272                 }
1273                 curRide->breakdown_reason = curRide->breakdown_reason_pending;
1274             }
1275         }
1276         else
1277         {
1278             vehicle->restraints_position = std::max(vehicle->restraints_position - 20, 0);
1279             if (vehicle->restraints_position == 0)
1280             {
1281                 continue;
1282             }
1283         }
1284         vehicle->Invalidate();
1285         restraintsClosed = false;
1286     }
1287 
1288     return restraintsClosed;
1289 }
1290 
1291 /**
1292  *
1293  *  rct2: 0x006D6A2C
1294  * @returns true when all open
1295  */
OpenRestraints()1296 bool Vehicle::OpenRestraints()
1297 {
1298     int32_t restraintsOpen = true;
1299     for (Vehicle* vehicle = GetEntity<Vehicle>(sprite_index); vehicle != nullptr;
1300          vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
1301     {
1302         vehicle->SwingPosition = 0;
1303         vehicle->SwingSpeed = 0;
1304         vehicle->SwingSprite = 0;
1305 
1306         auto curRide = vehicle->GetRide();
1307         if (curRide == nullptr)
1308             continue;
1309 
1310         auto rideEntry = vehicle->GetRideEntry();
1311         if (rideEntry == nullptr)
1312         {
1313             continue;
1314         }
1315 
1316         rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle->vehicle_type];
1317 
1318         if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_SPINNING)
1319         {
1320             // If the vehicle is a spinner it must be spinning slow
1321             // For vehicles without additional frames there are 4 rotations it can unload from
1322             // For vehicles with additional frames it must be facing forward
1323             if (abs(vehicle->spin_speed) <= VEHICLE_MAX_SPIN_SPEED_FOR_STOPPING && !(vehicle->spin_sprite & 0x30)
1324                 && (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_SPINNING_ADDITIONAL_FRAMES) || !(vehicle->spin_sprite & 0xF8)))
1325             {
1326                 vehicle->spin_speed = 0;
1327             }
1328             else
1329             {
1330                 restraintsOpen = false;
1331 
1332                 if (abs(vehicle->spin_speed) < VEHICLE_STOPPING_SPIN_SPEED)
1333                 {
1334                     // Note will look odd if spinning right.
1335                     vehicle->spin_speed = VEHICLE_STOPPING_SPIN_SPEED;
1336                 }
1337                 int16_t value = vehicle->spin_speed / 256;
1338                 vehicle->spin_sprite += value;
1339                 vehicle->spin_speed -= value;
1340 
1341                 vehicle->Invalidate();
1342                 continue;
1343             }
1344         }
1345         if (vehicleEntry->animation == VEHICLE_ENTRY_ANIMATION_OBSERVATION_TOWER && vehicle->animation_frame != 0)
1346         {
1347             if (vehicle->animationState <= 0xCCCC)
1348             {
1349                 vehicle->animationState += 0x3333;
1350             }
1351             else
1352             {
1353                 vehicle->animationState = 0;
1354                 vehicle->animation_frame++;
1355                 vehicle->animation_frame &= 7;
1356                 vehicle->Invalidate();
1357             }
1358             restraintsOpen = false;
1359             continue;
1360         }
1361         if (vehicleEntry->animation == VEHICLE_ENTRY_ANIMATION_ANIMAL_FLYING
1362             && (vehicle->animation_frame != 0 || vehicle->animationState > 0))
1363         {
1364             vehicle->UpdateAnimationAnimalFlying();
1365             restraintsOpen = false;
1366             continue;
1367         }
1368 
1369         if (vehicle->HasUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_CAR) && vehicle->restraints_position != 0xFF
1370             && (curRide->breakdown_reason_pending == BREAKDOWN_RESTRAINTS_STUCK_CLOSED
1371                 || curRide->breakdown_reason_pending == BREAKDOWN_DOORS_STUCK_CLOSED))
1372         {
1373             if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN))
1374             {
1375                 curRide->lifecycle_flags |= RIDE_LIFECYCLE_BROKEN_DOWN;
1376 
1377                 ride_breakdown_add_news_item(curRide);
1378 
1379                 curRide->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST
1380                     | RIDE_INVALIDATE_RIDE_MAINTENANCE;
1381 
1382                 curRide->mechanic_status = RIDE_MECHANIC_STATUS_CALLING;
1383 
1384                 Vehicle* broken_vehicle = GetEntity<Vehicle>(curRide->vehicles[curRide->broken_vehicle]);
1385                 if (broken_vehicle != nullptr)
1386                 {
1387                     curRide->inspection_station = broken_vehicle->current_station;
1388                 }
1389                 curRide->breakdown_reason = curRide->breakdown_reason_pending;
1390             }
1391         }
1392         else
1393         {
1394             if (vehicle->restraints_position + 20 > 0xFF)
1395             {
1396                 vehicle->restraints_position = 255;
1397                 continue;
1398             }
1399             vehicle->restraints_position += 20;
1400         }
1401         vehicle->Invalidate();
1402         restraintsOpen = false;
1403     }
1404 
1405     return restraintsOpen;
1406 }
1407 
1408 /**
1409  *
1410  *  rct2: 0x006D6D1F
1411  */
UpdateMeasurements()1412 void Vehicle::UpdateMeasurements()
1413 {
1414     auto curRide = GetRide();
1415     if (curRide == nullptr)
1416         return;
1417 
1418     if (status == Vehicle::Status::TravellingBoat)
1419     {
1420         curRide->lifecycle_flags |= RIDE_LIFECYCLE_TESTED;
1421         curRide->lifecycle_flags |= RIDE_LIFECYCLE_NO_RAW_STATS;
1422         curRide->lifecycle_flags &= ~RIDE_LIFECYCLE_TEST_IN_PROGRESS;
1423         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_TESTING);
1424         window_invalidate_by_number(WC_RIDE, EnumValue(ride));
1425         return;
1426     }
1427 
1428     if (curRide->current_test_station == STATION_INDEX_NULL)
1429         return;
1430 
1431     if (!ride_get_entrance_location(curRide, curRide->current_test_station).IsNull())
1432     {
1433         uint8_t test_segment = curRide->current_test_segment;
1434 
1435         curRide->average_speed_test_timeout++;
1436         if (curRide->average_speed_test_timeout >= 32)
1437             curRide->average_speed_test_timeout = 0;
1438 
1439         int32_t absVelocity = abs(velocity);
1440         if (absVelocity > curRide->max_speed)
1441         {
1442             curRide->max_speed = absVelocity;
1443         }
1444 
1445         if (curRide->average_speed_test_timeout == 0 && absVelocity > 0x8000)
1446         {
1447             curRide->average_speed = add_clamp_int32_t(curRide->average_speed, absVelocity);
1448             curRide->stations[test_segment].SegmentTime++;
1449         }
1450 
1451         int32_t distance = abs(((velocity + acceleration) >> 10) * 42);
1452         if (var_CE == 0)
1453         {
1454             curRide->stations[test_segment].SegmentLength = add_clamp_int32_t(
1455                 curRide->stations[test_segment].SegmentLength, distance);
1456         }
1457 
1458         if (curRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_G_FORCES))
1459         {
1460             auto gForces = GetGForces();
1461             gForces.VerticalG += curRide->previous_vertical_g;
1462             gForces.LateralG += curRide->previous_lateral_g;
1463             gForces.VerticalG /= 2;
1464             gForces.LateralG /= 2;
1465 
1466             curRide->previous_vertical_g = gForces.VerticalG;
1467             curRide->previous_lateral_g = gForces.LateralG;
1468             if (gForces.VerticalG <= 0)
1469             {
1470                 curRide->total_air_time++;
1471             }
1472 
1473             if (gForces.VerticalG > curRide->max_positive_vertical_g)
1474                 curRide->max_positive_vertical_g = gForces.VerticalG;
1475 
1476             if (gForces.VerticalG < curRide->max_negative_vertical_g)
1477                 curRide->max_negative_vertical_g = gForces.VerticalG;
1478 
1479             gForces.LateralG = std::abs(gForces.LateralG);
1480             curRide->max_lateral_g = std::max(curRide->max_lateral_g, static_cast<fixed16_2dp>(gForces.LateralG));
1481         }
1482     }
1483 
1484     // If we have already evaluated this track piece skip to next section
1485     TileCoordsXYZ curTrackLoc{ TrackLocation };
1486     if (curTrackLoc != curRide->CurTestTrackLocation)
1487     {
1488         curRide->CurTestTrackLocation = curTrackLoc;
1489 
1490         if (ride_get_entrance_location(curRide, curRide->current_test_station).IsNull())
1491             return;
1492 
1493         auto trackElemType = GetTrackType();
1494         if (trackElemType == TrackElemType::PoweredLift || HasUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL))
1495         {
1496             if (!(curRide->testing_flags & RIDE_TESTING_POWERED_LIFT))
1497             {
1498                 curRide->testing_flags |= RIDE_TESTING_POWERED_LIFT;
1499                 if (curRide->drops + 64 < 0xFF)
1500                 {
1501                     curRide->drops += 64;
1502                 }
1503             }
1504         }
1505         else
1506         {
1507             curRide->testing_flags &= ~RIDE_TESTING_POWERED_LIFT;
1508         }
1509 
1510         if (curRide->type == RIDE_TYPE_WATER_COASTER)
1511         {
1512             if (trackElemType >= TrackElemType::FlatCovered && trackElemType <= TrackElemType::RightQuarterTurn3TilesCovered)
1513             {
1514                 curRide->special_track_elements |= RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS;
1515             }
1516         }
1517 
1518         switch (trackElemType)
1519         {
1520             case TrackElemType::Rapids:
1521             case TrackElemType::SpinningTunnel:
1522                 curRide->special_track_elements |= RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS;
1523                 break;
1524             case TrackElemType::Waterfall:
1525             case TrackElemType::LogFlumeReverser:
1526                 curRide->special_track_elements |= RIDE_ELEMENT_REVERSER_OR_WATERFALL;
1527                 break;
1528             case TrackElemType::Whirlpool:
1529                 curRide->special_track_elements |= RIDE_ELEMENT_WHIRLPOOL;
1530                 break;
1531             case TrackElemType::Watersplash:
1532                 if (velocity >= 0xB0000)
1533                 {
1534                     curRide->special_track_elements |= RIDE_ELEMENT_TUNNEL_SPLASH_OR_RAPIDS;
1535                 }
1536         }
1537 
1538         const auto& ted = GetTrackElementDescriptor(trackElemType);
1539         uint16_t trackFlags = ted.Flags;
1540 
1541         uint32_t testingFlags = curRide->testing_flags;
1542         if (testingFlags & RIDE_TESTING_TURN_LEFT && trackFlags & TRACK_ELEM_FLAG_TURN_LEFT)
1543         {
1544             // 0x800 as this is masked to CURRENT_TURN_COUNT_MASK
1545             curRide->turn_count_default += 0x800;
1546         }
1547         else if (testingFlags & RIDE_TESTING_TURN_RIGHT && trackFlags & TRACK_ELEM_FLAG_TURN_RIGHT)
1548         {
1549             // 0x800 as this is masked to CURRENT_TURN_COUNT_MASK
1550             curRide->turn_count_default += 0x800;
1551         }
1552         else if (testingFlags & RIDE_TESTING_TURN_RIGHT || testingFlags & RIDE_TESTING_TURN_LEFT)
1553         {
1554             curRide->testing_flags &= ~(
1555                 RIDE_TESTING_TURN_LEFT | RIDE_TESTING_TURN_RIGHT | RIDE_TESTING_TURN_BANKED | RIDE_TESTING_TURN_SLOPED);
1556 
1557             uint8_t turnType = 1;
1558             if (!(testingFlags & RIDE_TESTING_TURN_BANKED))
1559             {
1560                 turnType = 2;
1561                 if (!(testingFlags & RIDE_TESTING_TURN_SLOPED))
1562                 {
1563                     turnType = 0;
1564                 }
1565             }
1566             switch (curRide->turn_count_default >> 11)
1567             {
1568                 case 0:
1569                     increment_turn_count_1_element(curRide, turnType);
1570                     break;
1571                 case 1:
1572                     increment_turn_count_2_elements(curRide, turnType);
1573                     break;
1574                 case 2:
1575                     increment_turn_count_3_elements(curRide, turnType);
1576                     break;
1577                 default:
1578                     increment_turn_count_4_plus_elements(curRide, turnType);
1579                     break;
1580             }
1581         }
1582         else
1583         {
1584             if (trackFlags & TRACK_ELEM_FLAG_TURN_LEFT)
1585             {
1586                 curRide->testing_flags |= RIDE_TESTING_TURN_LEFT;
1587                 curRide->turn_count_default &= ~CURRENT_TURN_COUNT_MASK;
1588 
1589                 if (trackFlags & TRACK_ELEM_FLAG_TURN_BANKED)
1590                 {
1591                     curRide->testing_flags |= RIDE_TESTING_TURN_BANKED;
1592                 }
1593                 if (trackFlags & TRACK_ELEM_FLAG_TURN_SLOPED)
1594                 {
1595                     curRide->testing_flags |= RIDE_TESTING_TURN_SLOPED;
1596                 }
1597             }
1598 
1599             if (trackFlags & TRACK_ELEM_FLAG_TURN_RIGHT)
1600             {
1601                 curRide->testing_flags |= RIDE_TESTING_TURN_RIGHT;
1602                 curRide->turn_count_default &= ~CURRENT_TURN_COUNT_MASK;
1603 
1604                 if (trackFlags & TRACK_ELEM_FLAG_TURN_BANKED)
1605                 {
1606                     curRide->testing_flags |= RIDE_TESTING_TURN_BANKED;
1607                 }
1608                 if (trackFlags & TRACK_ELEM_FLAG_TURN_SLOPED)
1609                 {
1610                     curRide->testing_flags |= RIDE_TESTING_TURN_SLOPED;
1611                 }
1612             }
1613         }
1614 
1615         if (testingFlags & RIDE_TESTING_DROP_DOWN)
1616         {
1617             if (velocity < 0 || !(trackFlags & TRACK_ELEM_FLAG_DOWN))
1618             {
1619                 curRide->testing_flags &= ~RIDE_TESTING_DROP_DOWN;
1620 
1621                 int16_t curZ = z / COORDS_Z_STEP - curRide->start_drop_height;
1622                 if (curZ < 0)
1623                 {
1624                     curZ = abs(curZ);
1625                     if (curZ > curRide->highest_drop_height)
1626                     {
1627                         curRide->highest_drop_height = static_cast<uint8_t>(curZ);
1628                     }
1629                 }
1630             }
1631         }
1632         else if (trackFlags & TRACK_ELEM_FLAG_DOWN && velocity >= 0)
1633         {
1634             curRide->testing_flags &= ~RIDE_TESTING_DROP_UP;
1635             curRide->testing_flags |= RIDE_TESTING_DROP_DOWN;
1636 
1637             uint8_t drops = curRide->drops & 0x3F;
1638             if (drops != 0x3F)
1639                 drops++;
1640             curRide->drops &= ~0x3F;
1641             curRide->drops |= drops;
1642 
1643             curRide->start_drop_height = z / COORDS_Z_STEP;
1644             testingFlags &= ~RIDE_TESTING_DROP_UP;
1645         }
1646 
1647         if (testingFlags & RIDE_TESTING_DROP_UP)
1648         {
1649             if (velocity > 0 || !(trackFlags & TRACK_ELEM_FLAG_UP))
1650             {
1651                 curRide->testing_flags &= ~RIDE_TESTING_DROP_UP;
1652 
1653                 int16_t curZ = z / COORDS_Z_STEP - curRide->start_drop_height;
1654                 if (curZ < 0)
1655                 {
1656                     curZ = abs(curZ);
1657                     if (curZ > curRide->highest_drop_height)
1658                     {
1659                         curRide->highest_drop_height = static_cast<uint8_t>(curZ);
1660                     }
1661                 }
1662             }
1663         }
1664         else if (trackFlags & TRACK_ELEM_FLAG_UP && velocity <= 0)
1665         {
1666             curRide->testing_flags &= ~RIDE_TESTING_DROP_DOWN;
1667             curRide->testing_flags |= RIDE_TESTING_DROP_UP;
1668 
1669             uint8_t drops = curRide->drops & 0x3F;
1670             if (drops != 0x3F)
1671                 drops++;
1672             curRide->drops &= ~0x3F;
1673             curRide->drops |= drops;
1674 
1675             curRide->start_drop_height = z / COORDS_Z_STEP;
1676         }
1677 
1678         if (curRide->type == RIDE_TYPE_MINI_GOLF)
1679         {
1680             if (trackFlags & TRACK_ELEM_FLAG_IS_GOLF_HOLE)
1681             {
1682                 if (curRide->holes < MAX_GOLF_HOLES)
1683                     curRide->holes++;
1684             }
1685         }
1686         else
1687         {
1688             if (trackFlags & TRACK_ELEM_FLAG_NORMAL_TO_INVERSION)
1689             {
1690                 if (curRide->inversions < MAX_INVERSIONS)
1691                     curRide->inversions++;
1692             }
1693         }
1694 
1695         if (trackFlags & TRACK_ELEM_FLAG_HELIX)
1696         {
1697             uint8_t helixes = ride_get_helix_sections(curRide);
1698             if (helixes != MAX_HELICES)
1699                 helixes++;
1700 
1701             curRide->special_track_elements &= ~0x1F;
1702             curRide->special_track_elements |= helixes;
1703         }
1704     }
1705 
1706     if (ride_get_entrance_location(curRide, curRide->current_test_station).IsNull())
1707         return;
1708 
1709     if (x == LOCATION_NULL)
1710     {
1711         curRide->testing_flags &= ~RIDE_TESTING_SHELTERED;
1712         return;
1713     }
1714 
1715     auto surfaceElement = map_get_surface_element_at(CoordsXY{ x, y });
1716     // If vehicle above ground.
1717     if (surfaceElement != nullptr && surfaceElement->GetBaseZ() <= z)
1718     {
1719         // Set tile_element to first element. Since elements aren't always ordered by base height,
1720         // we must start at the first element and iterate through each tile element.
1721         auto tileElement = map_get_first_element_at(CoordsXY{ x, y });
1722         if (tileElement == nullptr)
1723             return;
1724 
1725         bool coverFound = false;
1726         do
1727         {
1728             // If the tile_element is lower than the vehicle, continue (don't set flag)
1729             if (tileElement->GetBaseZ() <= z)
1730                 continue;
1731 
1732             if (tileElement->GetType() == TILE_ELEMENT_TYPE_LARGE_SCENERY)
1733             {
1734                 coverFound = true;
1735                 break;
1736             }
1737 
1738             if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH)
1739             {
1740                 coverFound = true;
1741                 break;
1742             }
1743 
1744             if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
1745                 continue;
1746 
1747             auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
1748             if (sceneryEntry == nullptr)
1749                 continue;
1750 
1751             if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))
1752             {
1753                 coverFound = true;
1754                 break;
1755             }
1756             // Iterate through each tile_element.
1757         } while (!(tileElement++)->IsLastForTile());
1758 
1759         if (!coverFound)
1760         {
1761             curRide->testing_flags &= ~RIDE_TESTING_SHELTERED;
1762             return;
1763         }
1764     }
1765 
1766     if (!(curRide->testing_flags & RIDE_TESTING_SHELTERED))
1767     {
1768         curRide->testing_flags |= RIDE_TESTING_SHELTERED;
1769 
1770         curRide->IncreaseNumShelteredSections();
1771 
1772         if (Pitch != 0)
1773         {
1774             curRide->num_sheltered_sections |= ShelteredSectionsBits::RotatingWhileSheltered;
1775         }
1776 
1777         if (bank_rotation != 0)
1778         {
1779             curRide->num_sheltered_sections |= ShelteredSectionsBits::BankingWhileSheltered;
1780         }
1781     }
1782 
1783     int32_t distance = ((velocity + acceleration) >> 10) * 42;
1784     if (distance < 0)
1785         return;
1786 
1787     curRide->sheltered_length = add_clamp_int32_t(curRide->sheltered_length, distance);
1788 }
1789 
1790 struct SoundIdVolume
1791 {
1792     OpenRCT2::Audio::SoundId id;
1793     uint8_t volume;
1794 };
1795 
sub_6D7AC0(OpenRCT2::Audio::SoundId currentSoundId,uint8_t currentVolume,OpenRCT2::Audio::SoundId targetSoundId,uint8_t targetVolume)1796 static SoundIdVolume sub_6D7AC0(
1797     OpenRCT2::Audio::SoundId currentSoundId, uint8_t currentVolume, OpenRCT2::Audio::SoundId targetSoundId,
1798     uint8_t targetVolume)
1799 {
1800     if (currentSoundId != OpenRCT2::Audio::SoundId::Null)
1801     {
1802         if (currentSoundId == targetSoundId)
1803         {
1804             currentVolume = std::min<int32_t>(currentVolume + 15, targetVolume);
1805             return { currentSoundId, currentVolume };
1806         }
1807 
1808         currentVolume -= 9;
1809         if (currentVolume >= 80)
1810             return { currentSoundId, currentVolume };
1811     }
1812 
1813     // Begin sound at quarter volume
1814     currentSoundId = targetSoundId;
1815     currentVolume = targetVolume == 255 ? 255 : targetVolume / 4;
1816 
1817     return { currentSoundId, currentVolume };
1818 }
1819 
GetLiftHillSound(Ride * curRide,SoundIdVolume & curSound)1820 void Vehicle::GetLiftHillSound(Ride* curRide, SoundIdVolume& curSound)
1821 {
1822     scream_sound_id = OpenRCT2::Audio::SoundId::Null;
1823     if (curRide->type < std::size(RideTypeDescriptors))
1824     {
1825         // Get lift hill sound
1826         curSound.id = GetRideTypeDescriptor(curRide->type).LiftData.sound_id;
1827         curSound.volume = 243;
1828         if (!(sound2_flags & VEHICLE_SOUND2_FLAGS_LIFT_HILL))
1829             curSound.id = OpenRCT2::Audio::SoundId::Null;
1830     }
1831 }
1832 
1833 /**
1834  *
1835  *  rct2: 0x006D77F2
1836  */
Update()1837 void Vehicle::Update()
1838 {
1839     // The cable lift uses a ride entry index of NULL
1840     if (ride_subtype == OBJECT_ENTRY_INDEX_NULL)
1841     {
1842         CableLiftUpdate();
1843         return;
1844     }
1845 
1846     auto rideEntry = GetRideEntry();
1847     if (rideEntry == nullptr)
1848         return;
1849 
1850     auto curRide = GetRide();
1851     if (curRide == nullptr)
1852         return;
1853 
1854     if (curRide->type >= RIDE_TYPE_COUNT)
1855         return;
1856 
1857     if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_TESTING))
1858         UpdateMeasurements();
1859 
1860     _vehicleBreakdown = 255;
1861     if (curRide->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN))
1862     {
1863         _vehicleBreakdown = curRide->breakdown_reason_pending;
1864         auto vehicleEntry = &rideEntry->vehicles[vehicle_type];
1865         if ((vehicleEntry->flags & VEHICLE_ENTRY_FLAG_POWERED) && curRide->breakdown_reason_pending == BREAKDOWN_SAFETY_CUT_OUT)
1866         {
1867             if (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_WATER_RIDE) || (Pitch == 2 && velocity <= 0x20000))
1868             {
1869                 SetUpdateFlag(VEHICLE_UPDATE_FLAG_ZERO_VELOCITY);
1870             }
1871         }
1872     }
1873 
1874     switch (status)
1875     {
1876         case Vehicle::Status::MovingToEndOfStation:
1877             UpdateMovingToEndOfStation();
1878             break;
1879         case Vehicle::Status::WaitingForPassengers:
1880             UpdateWaitingForPassengers();
1881             break;
1882         case Vehicle::Status::WaitingToDepart:
1883             UpdateWaitingToDepart();
1884             break;
1885         case Vehicle::Status::Crashing:
1886         case Vehicle::Status::Crashed:
1887             UpdateCrash();
1888             break;
1889         case Vehicle::Status::TravellingDodgems:
1890             UpdateDodgemsMode();
1891             break;
1892         case Vehicle::Status::Swinging:
1893             UpdateSwinging();
1894             break;
1895         case Vehicle::Status::SimulatorOperating:
1896             UpdateSimulatorOperating();
1897             break;
1898         case Vehicle::Status::TopSpinOperating:
1899             UpdateTopSpinOperating();
1900             break;
1901         case Vehicle::Status::FerrisWheelRotating:
1902             UpdateFerrisWheelRotating();
1903             break;
1904         case Vehicle::Status::SpaceRingsOperating:
1905             UpdateSpaceRingsOperating();
1906             break;
1907         case Vehicle::Status::HauntedHouseOperating:
1908             UpdateHauntedHouseOperating();
1909             break;
1910         case Vehicle::Status::CrookedHouseOperating:
1911             UpdateCrookedHouseOperating();
1912             break;
1913         case Vehicle::Status::Rotating:
1914             UpdateRotating();
1915             break;
1916         case Vehicle::Status::Departing:
1917             UpdateDeparting();
1918             break;
1919         case Vehicle::Status::Travelling:
1920             UpdateTravelling();
1921             break;
1922         case Vehicle::Status::TravellingCableLift:
1923             UpdateTravellingCableLift();
1924             break;
1925         case Vehicle::Status::TravellingBoat:
1926             UpdateTravellingBoat();
1927             break;
1928         case Vehicle::Status::Arriving:
1929             UpdateArriving();
1930             break;
1931         case Vehicle::Status::UnloadingPassengers:
1932             UpdateUnloadingPassengers();
1933             break;
1934         case Vehicle::Status::WaitingForCableLift:
1935             UpdateWaitingForCableLift();
1936             break;
1937         case Vehicle::Status::ShowingFilm:
1938             UpdateShowingFilm();
1939             break;
1940         case Vehicle::Status::DoingCircusShow:
1941             UpdateDoingCircusShow();
1942         default:
1943             break;
1944     }
1945 
1946     UpdateSound();
1947 }
1948 
1949 /**
1950  *
1951  *  rct2: 0x006D7BCC
1952  */
UpdateMovingToEndOfStation()1953 void Vehicle::UpdateMovingToEndOfStation()
1954 {
1955     auto curRide = GetRide();
1956     if (curRide == nullptr)
1957         return;
1958 
1959     int32_t curFlags = 0;
1960     int32_t station = 0;
1961 
1962     switch (curRide->mode)
1963     {
1964         case RideMode::UpwardLaunch:
1965         case RideMode::RotatingLift:
1966         case RideMode::DownwardLaunch:
1967         case RideMode::FreefallDrop:
1968             if (velocity >= -131940)
1969             {
1970                 acceleration = -3298;
1971             }
1972             if (velocity < -131940)
1973             {
1974                 velocity -= velocity / 16;
1975                 acceleration = 0;
1976             }
1977             curFlags = UpdateTrackMotion(&station);
1978             if (!(curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_5))
1979                 break;
1980             [[fallthrough]];
1981         case RideMode::Dodgems:
1982         case RideMode::Swing:
1983         case RideMode::Rotation:
1984         case RideMode::ForwardRotation:
1985         case RideMode::BackwardRotation:
1986         case RideMode::FilmAvengingAviators:
1987         case RideMode::FilmThrillRiders:
1988         case RideMode::Beginners:
1989         case RideMode::Intense:
1990         case RideMode::Berserk:
1991         case RideMode::MouseTails3DFilm:
1992         case RideMode::StormChasers3DFilm:
1993         case RideMode::SpaceRaiders3DFilm:
1994         case RideMode::SpaceRings:
1995         case RideMode::HauntedHouse:
1996         case RideMode::CrookedHouse:
1997         case RideMode::Circus:
1998             current_station = 0;
1999             velocity = 0;
2000             acceleration = 0;
2001             SetState(Vehicle::Status::WaitingForPassengers);
2002             break;
2003         default:
2004         {
2005             rct_ride_entry* rideEntry = GetRideEntry();
2006             if (rideEntry == nullptr)
2007             {
2008                 return;
2009             }
2010 
2011             rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle_type];
2012 
2013             if (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_POWERED))
2014             {
2015                 if (velocity <= 131940)
2016                 {
2017                     acceleration = 3298;
2018                 }
2019             }
2020             if (velocity > 131940)
2021             {
2022                 velocity -= velocity / 16;
2023                 acceleration = 0;
2024             }
2025 
2026             curFlags = UpdateTrackMotion(&station);
2027 
2028             if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_1)
2029             {
2030                 velocity = 0;
2031                 acceleration = 0;
2032                 sub_state++;
2033 
2034                 if (curRide->mode == RideMode::Race && sub_state >= 40)
2035                 {
2036                     SetState(Vehicle::Status::WaitingForPassengers);
2037                     break;
2038                 }
2039             }
2040             else
2041             {
2042                 if (velocity > 98955)
2043                 {
2044                     sub_state = 0;
2045                 }
2046             }
2047 
2048             if (!(curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION))
2049                 break;
2050 
2051             current_station = station;
2052             velocity = 0;
2053             acceleration = 0;
2054             SetState(Vehicle::Status::WaitingForPassengers);
2055             break;
2056         }
2057     }
2058 }
2059 
2060 /**
2061  *
2062  *  rct2: 0x006D7FB4
2063  */
TrainReadyToDepart(uint8_t num_peeps_on_train,uint8_t num_used_seats)2064 void Vehicle::TrainReadyToDepart(uint8_t num_peeps_on_train, uint8_t num_used_seats)
2065 {
2066     if (num_peeps_on_train != num_used_seats)
2067         return;
2068 
2069     auto curRide = GetRide();
2070     if (curRide == nullptr)
2071         return;
2072 
2073     if (curRide->status == RideStatus::Open && !(curRide->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
2074         && !HasUpdateFlag(VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART))
2075     {
2076         return;
2077     }
2078 
2079     if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN))
2080     {
2081         // Original code did not check if the ride was a boat hire, causing empty boats to leave the platform when closing a
2082         // Boat Hire with passengers on it.
2083         if (curRide->status != RideStatus::Closed || (curRide->num_riders != 0 && curRide->type != RIDE_TYPE_BOAT_HIRE))
2084         {
2085             curRide->stations[current_station].TrainAtStation = RideStation::NO_TRAIN;
2086             sub_state = 2;
2087             return;
2088         }
2089     }
2090 
2091     if (curRide->mode == RideMode::ForwardRotation || curRide->mode == RideMode::BackwardRotation)
2092     {
2093         uint8_t seat = ((-Pitch) / 8) & 0xF;
2094         if (peep[seat] != SPRITE_INDEX_NULL)
2095         {
2096             curRide->stations[current_station].TrainAtStation = RideStation::NO_TRAIN;
2097             SetState(Vehicle::Status::UnloadingPassengers);
2098             return;
2099         }
2100 
2101         if (num_peeps == 0)
2102             return;
2103 
2104         curRide->stations[current_station].TrainAtStation = RideStation::NO_TRAIN;
2105         sub_state = 2;
2106         return;
2107     }
2108 
2109     if (num_peeps_on_train == 0)
2110         return;
2111 
2112     curRide->stations[current_station].TrainAtStation = RideStation::NO_TRAIN;
2113     SetState(Vehicle::Status::WaitingForPassengers);
2114 }
2115 
ride_get_train_index_from_vehicle(Ride * ride,uint16_t spriteIndex)2116 static std::optional<uint32_t> ride_get_train_index_from_vehicle(Ride* ride, uint16_t spriteIndex)
2117 {
2118     uint32_t trainIndex = 0;
2119     while (ride->vehicles[trainIndex] != spriteIndex)
2120     {
2121         trainIndex++;
2122         if (trainIndex >= ride->num_vehicles)
2123         {
2124             // This should really return nullopt, but doing so
2125             // would break some hacked parks that hide track by setting tracked rides'
2126             // track type to, e.g., Crooked House
2127             break;
2128         }
2129         if (trainIndex >= std::size(ride->vehicles))
2130         {
2131             return std::nullopt;
2132         }
2133     }
2134     return { trainIndex };
2135 }
2136 
2137 /**
2138  *
2139  *  rct2: 0x006D7DA1
2140  */
UpdateWaitingForPassengers()2141 void Vehicle::UpdateWaitingForPassengers()
2142 {
2143     velocity = 0;
2144 
2145     auto curRide = GetRide();
2146     if (curRide == nullptr)
2147         return;
2148 
2149     if (sub_state == 0)
2150     {
2151         if (!OpenRestraints())
2152             return;
2153 
2154         if (ride_get_entrance_location(curRide, current_station).IsNull())
2155         {
2156             curRide->stations[current_station].TrainAtStation = RideStation::NO_TRAIN;
2157             sub_state = 2;
2158             return;
2159         }
2160 
2161         auto trainIndex = ride_get_train_index_from_vehicle(curRide, sprite_index);
2162         if (!trainIndex.has_value())
2163         {
2164             return;
2165         }
2166 
2167         if (curRide->stations[current_station].TrainAtStation != RideStation::NO_TRAIN)
2168             return;
2169 
2170         curRide->stations[current_station].TrainAtStation = trainIndex.value();
2171         sub_state = 1;
2172         time_waiting = 0;
2173 
2174         Invalidate();
2175         return;
2176     }
2177     if (sub_state == 1)
2178     {
2179         if (time_waiting != 0xFFFF)
2180             time_waiting++;
2181 
2182         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART);
2183 
2184         // 0xF64E31, 0xF64E32, 0xF64E33
2185         uint8_t num_peeps_on_train = 0, num_used_seats_on_train = 0, num_seats_on_train = 0;
2186 
2187         for (const Vehicle* trainCar = GetEntity<Vehicle>(sprite_index); trainCar != nullptr;
2188              trainCar = GetEntity<Vehicle>(trainCar->next_vehicle_on_train))
2189         {
2190             num_peeps_on_train += trainCar->num_peeps;
2191             num_used_seats_on_train += trainCar->next_free_seat;
2192             num_seats_on_train += trainCar->num_seats;
2193         }
2194 
2195         num_seats_on_train &= 0x7F;
2196 
2197         if (curRide->SupportsStatus(RideStatus::Testing))
2198         {
2199             if (time_waiting < 20)
2200             {
2201                 TrainReadyToDepart(num_peeps_on_train, num_used_seats_on_train);
2202                 return;
2203             }
2204         }
2205         else
2206         {
2207             if (num_peeps_on_train == 0)
2208             {
2209                 TrainReadyToDepart(num_peeps_on_train, num_used_seats_on_train);
2210                 return;
2211             }
2212         }
2213 
2214         if (curRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS))
2215         {
2216             if (curRide->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH)
2217             {
2218                 if (curRide->min_waiting_time * 32 > time_waiting)
2219                 {
2220                     TrainReadyToDepart(num_peeps_on_train, num_used_seats_on_train);
2221                     return;
2222                 }
2223             }
2224             if (curRide->depart_flags & RIDE_DEPART_WAIT_FOR_MAXIMUM_LENGTH)
2225             {
2226                 if (curRide->max_waiting_time * 32 < time_waiting)
2227                 {
2228                     SetUpdateFlag(VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART);
2229                     TrainReadyToDepart(num_peeps_on_train, num_used_seats_on_train);
2230                     return;
2231                 }
2232             }
2233         }
2234 
2235         if (curRide->depart_flags & RIDE_DEPART_LEAVE_WHEN_ANOTHER_ARRIVES)
2236         {
2237             for (auto train_id : curRide->vehicles)
2238             {
2239                 if (train_id == sprite_index)
2240                     continue;
2241 
2242                 Vehicle* train = GetEntity<Vehicle>(train_id);
2243                 if (train == nullptr)
2244                     continue;
2245 
2246                 if (train->status == Vehicle::Status::UnloadingPassengers
2247                     || train->status == Vehicle::Status::MovingToEndOfStation)
2248                 {
2249                     if (train->current_station == current_station)
2250                     {
2251                         SetUpdateFlag(VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART);
2252                         TrainReadyToDepart(num_peeps_on_train, num_used_seats_on_train);
2253                         return;
2254                     }
2255                 }
2256             }
2257         }
2258 
2259         if (curRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LOAD_OPTIONS)
2260             && curRide->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD)
2261         {
2262             if (num_peeps_on_train == num_seats_on_train)
2263             {
2264                 SetUpdateFlag(VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART);
2265                 TrainReadyToDepart(num_peeps_on_train, num_used_seats_on_train);
2266                 return;
2267             }
2268 
2269             // any load: load=4 , full: load=3 , 3/4s: load=2 , half: load=1 , quarter: load=0
2270             uint8_t load = curRide->depart_flags & RIDE_DEPART_WAIT_FOR_LOAD_MASK;
2271 
2272             // We want to wait for ceiling((load+1)/4 * num_seats_on_train) peeps, the +3 below is used instead of
2273             // ceil() to prevent issues on different cpus/platforms with floats. Note that vanilla RCT1/2 rounded
2274             // down here; our change reflects the expected behaviour for waiting for a minimum load target (see #9987)
2275             uint8_t peepTarget = ((load + 1) * num_seats_on_train + 3) / 4;
2276 
2277             if (load == 4) // take care of "any load" special case
2278                 peepTarget = 1;
2279 
2280             if (num_peeps_on_train >= peepTarget)
2281                 SetUpdateFlag(VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART);
2282 
2283             TrainReadyToDepart(num_peeps_on_train, num_used_seats_on_train);
2284             return;
2285         }
2286 
2287         SetUpdateFlag(VEHICLE_UPDATE_FLAG_TRAIN_READY_DEPART);
2288         TrainReadyToDepart(num_peeps_on_train, num_used_seats_on_train);
2289         return;
2290     }
2291 
2292     if (!CloseRestraints())
2293         return;
2294 
2295     velocity = 0;
2296     ClearUpdateFlag(VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT);
2297 
2298     if (curRide->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS)
2299     {
2300         SetUpdateFlag(VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT);
2301     }
2302 
2303     SetState(Vehicle::Status::WaitingToDepart);
2304 }
2305 
2306 /**
2307  *
2308  *  rct2: 0x006D91BF
2309  */
UpdateDodgemsMode()2310 void Vehicle::UpdateDodgemsMode()
2311 {
2312     auto curRide = GetRide();
2313     if (curRide == nullptr)
2314         return;
2315 
2316     rct_ride_entry* rideEntry = GetRideEntry();
2317     if (rideEntry == nullptr)
2318     {
2319         return;
2320     }
2321     rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle_type];
2322 
2323     // Mark the dodgem as in use.
2324     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_DODGEM_INUSE_LIGHTS && animation_frame != 1)
2325     {
2326         animation_frame = 1;
2327         Invalidate();
2328     }
2329 
2330     UpdateMotionDodgems();
2331 
2332     // Update the length of time vehicle has been in dodgems mode
2333     if (sub_state++ == 0xFF)
2334     {
2335         var_CE++;
2336     }
2337 
2338     if (curRide->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING)
2339         return;
2340 
2341     // Mark the dodgem as not in use.
2342     animation_frame = 0;
2343     Invalidate();
2344     velocity = 0;
2345     acceleration = 0;
2346     SetState(Vehicle::Status::UnloadingPassengers);
2347 }
2348 
2349 /**
2350  *
2351  *  rct2: 0x006D80BE
2352  */
UpdateWaitingToDepart()2353 void Vehicle::UpdateWaitingToDepart()
2354 {
2355     auto curRide = GetRide();
2356     if (curRide == nullptr)
2357         return;
2358 
2359     bool shouldBreak = false;
2360     if (curRide->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
2361     {
2362         switch (curRide->breakdown_reason_pending)
2363         {
2364             case BREAKDOWN_RESTRAINTS_STUCK_CLOSED:
2365             case BREAKDOWN_RESTRAINTS_STUCK_OPEN:
2366             case BREAKDOWN_DOORS_STUCK_CLOSED:
2367             case BREAKDOWN_DOORS_STUCK_OPEN:
2368                 break;
2369             default:
2370                 shouldBreak = true;
2371                 break;
2372         }
2373     }
2374 
2375     bool skipCheck = false;
2376     if (shouldBreak || curRide->status != RideStatus::Open)
2377     {
2378         if (curRide->mode == RideMode::ForwardRotation || curRide->mode == RideMode::BackwardRotation)
2379         {
2380             uint8_t seat = ((-Pitch) >> 3) & 0xF;
2381             if (peep[seat * 2] == SPRITE_INDEX_NULL)
2382             {
2383                 if (num_peeps == 0)
2384                 {
2385                     skipCheck = true;
2386                 }
2387             }
2388             else
2389             {
2390                 if (!ride_get_exit_location(curRide, current_station).IsNull())
2391                 {
2392                     SetState(Vehicle::Status::UnloadingPassengers);
2393                     return;
2394                 }
2395             }
2396         }
2397         else
2398         {
2399             for (const Vehicle* trainCar = GetEntity<Vehicle>(sprite_index); trainCar != nullptr;
2400                  trainCar = GetEntity<Vehicle>(trainCar->next_vehicle_on_train))
2401             {
2402                 if (trainCar->num_peeps != 0)
2403                 {
2404                     if (!ride_get_exit_location(curRide, current_station).IsNull())
2405                     {
2406                         SetState(Vehicle::Status::UnloadingPassengers);
2407                         return;
2408                     }
2409                     break;
2410                 }
2411             }
2412         }
2413     }
2414 
2415     if (!skipCheck)
2416     {
2417         if (!(curRide->stations[current_station].Depart & STATION_DEPART_FLAG))
2418             return;
2419     }
2420 
2421     if (curRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_CAN_SYNCHRONISE_ADJACENT_STATIONS))
2422     {
2423         if (curRide->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS)
2424         {
2425             if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT))
2426             {
2427                 if (!CanDepartSynchronised())
2428                 {
2429                     return;
2430                 }
2431             }
2432         }
2433     }
2434 
2435     SetState(Vehicle::Status::Departing);
2436 
2437     if (curRide->lifecycle_flags & RIDE_LIFECYCLE_CABLE_LIFT)
2438     {
2439         CoordsXYE track;
2440         int32_t zUnused;
2441         int32_t direction;
2442 
2443         uint8_t trackDirection = GetTrackDirection();
2444         if (track_block_get_next_from_zero(TrackLocation, curRide, trackDirection, &track, &zUnused, &direction, false))
2445         {
2446             if (track.element->AsTrack()->HasCableLift())
2447             {
2448                 SetState(Vehicle::Status::WaitingForCableLift, sub_state);
2449             }
2450         }
2451     }
2452 
2453     switch (curRide->mode)
2454     {
2455         case RideMode::Dodgems:
2456             // Dodgems mode uses sub_state / var_CE to tell how long
2457             // the vehicle has been ridden.
2458             SetState(Vehicle::Status::TravellingDodgems);
2459             var_CE = 0;
2460             UpdateDodgemsMode();
2461             break;
2462         case RideMode::Swing:
2463             SetState(Vehicle::Status::Swinging);
2464             var_CE = 0;
2465             current_time = -1;
2466             UpdateSwinging();
2467             break;
2468         case RideMode::Rotation:
2469             SetState(Vehicle::Status::Rotating);
2470             var_CE = 0;
2471             current_time = -1;
2472             UpdateRotating();
2473             break;
2474         case RideMode::FilmAvengingAviators:
2475             SetState(Vehicle::Status::SimulatorOperating);
2476             current_time = -1;
2477             UpdateSimulatorOperating();
2478             break;
2479         case RideMode::FilmThrillRiders:
2480             SetState(Vehicle::Status::SimulatorOperating, 1);
2481             current_time = -1;
2482             UpdateSimulatorOperating();
2483             break;
2484         case RideMode::Beginners:
2485         case RideMode::Intense:
2486         case RideMode::Berserk:
2487             SetState(Vehicle::Status::TopSpinOperating, sub_state);
2488             switch (curRide->mode)
2489             {
2490                 case RideMode::Beginners:
2491                     sub_state = 0;
2492                     break;
2493                 case RideMode::Intense:
2494                     sub_state = 1;
2495                     break;
2496                 case RideMode::Berserk:
2497                     sub_state = 2;
2498                     break;
2499                 default:
2500                 {
2501                     // This is workaround for multiple compilation errors of type "enumeration value ‘RIDE_MODE_*' not handled
2502                     // in switch [-Werror=switch]"
2503                 }
2504             }
2505             current_time = -1;
2506             Pitch = 0;
2507             bank_rotation = 0;
2508             UpdateTopSpinOperating();
2509             break;
2510         case RideMode::ForwardRotation:
2511         case RideMode::BackwardRotation:
2512             SetState(Vehicle::Status::FerrisWheelRotating, Pitch);
2513             var_CE = 0;
2514             ferris_wheel_var_0 = 8;
2515             ferris_wheel_var_1 = 8;
2516             UpdateFerrisWheelRotating();
2517             break;
2518         case RideMode::MouseTails3DFilm:
2519         case RideMode::StormChasers3DFilm:
2520         case RideMode::SpaceRaiders3DFilm:
2521             SetState(Vehicle::Status::ShowingFilm, sub_state);
2522             switch (curRide->mode)
2523             {
2524                 case RideMode::MouseTails3DFilm:
2525                     sub_state = 0;
2526                     break;
2527                 case RideMode::StormChasers3DFilm:
2528                     sub_state = 1;
2529                     break;
2530                 case RideMode::SpaceRaiders3DFilm:
2531                     sub_state = 2;
2532                     break;
2533                 default:
2534                 {
2535                     // This is workaround for multiple compilation errors of type "enumeration value ‘RIDE_MODE_*' not handled
2536                     // in switch [-Werror=switch]"
2537                 }
2538             }
2539             current_time = -1;
2540             UpdateShowingFilm();
2541             break;
2542         case RideMode::Circus:
2543             SetState(Vehicle::Status::DoingCircusShow);
2544             current_time = -1;
2545             UpdateDoingCircusShow();
2546             break;
2547         case RideMode::SpaceRings:
2548             SetState(Vehicle::Status::SpaceRingsOperating);
2549             Pitch = 0;
2550             current_time = -1;
2551             UpdateSpaceRingsOperating();
2552             break;
2553         case RideMode::HauntedHouse:
2554             SetState(Vehicle::Status::HauntedHouseOperating);
2555             Pitch = 0;
2556             current_time = -1;
2557             UpdateHauntedHouseOperating();
2558             break;
2559         case RideMode::CrookedHouse:
2560             SetState(Vehicle::Status::CrookedHouseOperating);
2561             Pitch = 0;
2562             current_time = -1;
2563             UpdateCrookedHouseOperating();
2564             break;
2565         default:
2566             SetState(status);
2567             var_CE = 0;
2568             break;
2569     }
2570 }
2571 
2572 struct rct_synchronised_vehicle
2573 {
2574     ride_id_t ride_id;
2575     StationIndex stationIndex;
2576     uint16_t vehicle_id;
2577 };
2578 
2579 constexpr int32_t SYNCHRONISED_VEHICLE_COUNT = 16;
2580 
2581 // Synchronised vehicle info
2582 static rct_synchronised_vehicle _synchronisedVehicles[SYNCHRONISED_VEHICLE_COUNT] = {};
2583 
2584 static rct_synchronised_vehicle* _lastSynchronisedVehicle = nullptr;
2585 
2586 /**
2587  * Checks if a map position contains a synchronised ride station and adds the vehicle
2588  * to synchronise to the vehicle synchronisation list.
2589  *  rct2: 0x006DE1A4
2590  */
try_add_synchronised_station(const CoordsXYZ & coords)2591 static bool try_add_synchronised_station(const CoordsXYZ& coords)
2592 {
2593     // make sure we are in map bounds
2594     if (!map_is_location_valid(coords))
2595     {
2596         return false;
2597     }
2598 
2599     TileElement* tileElement = get_station_platform({ coords, coords.z + 2 * COORDS_Z_STEP });
2600     if (tileElement == nullptr)
2601     {
2602         /* No station platform element found,
2603          * so no station to synchronise */
2604         return false;
2605     }
2606 
2607     auto rideIndex = tileElement->AsTrack()->GetRideIndex();
2608     auto ride = get_ride(rideIndex);
2609     if (ride == nullptr || !(ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS))
2610     {
2611         /* Ride is not set to synchronise with adjacent stations. */
2612         return false;
2613     }
2614 
2615     /* From this point on, the ride of the map element is one that is set
2616      * to sync with adjacent stations, so it will return true.
2617      * Still to determine if a vehicle to sync can be identified. */
2618 
2619     auto stationIndex = tileElement->AsTrack()->GetStationIndex();
2620 
2621     rct_synchronised_vehicle* sv = _lastSynchronisedVehicle;
2622     sv->ride_id = rideIndex;
2623     sv->stationIndex = stationIndex;
2624     sv->vehicle_id = SPRITE_INDEX_NULL;
2625     _lastSynchronisedVehicle++;
2626 
2627     /* Ride vehicles are not on the track (e.g. ride is/was under
2628      * construction), so just return; vehicle_id for this station
2629      * is SPRITE_INDEX_NULL. */
2630     if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK))
2631     {
2632         return true;
2633     }
2634 
2635     /* Station is not ready to depart, so just return;
2636      * vehicle_id for this station is SPRITE_INDEX_NULL. */
2637     if (!(ride->stations[stationIndex].Depart & STATION_DEPART_FLAG))
2638     {
2639         return true;
2640     }
2641 
2642     // Look for a vehicle on this station waiting to depart.
2643     for (int32_t i = 0; i < ride->num_vehicles; i++)
2644     {
2645         auto* vehicle = GetEntity<Vehicle>(ride->vehicles[i]);
2646         if (vehicle == nullptr)
2647         {
2648             continue;
2649         }
2650 
2651         if (vehicle->status != Vehicle::Status::WaitingToDepart)
2652         {
2653             continue;
2654         }
2655         if (vehicle->sub_state != 0)
2656         {
2657             continue;
2658         }
2659         if (!vehicle->HasUpdateFlag(VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT))
2660         {
2661             continue;
2662         }
2663         if (vehicle->current_station != stationIndex)
2664         {
2665             continue;
2666         }
2667 
2668         sv->vehicle_id = vehicle->sprite_index;
2669         return true;
2670     }
2671 
2672     /* No vehicle found waiting to depart (with sync adjacent) at the
2673      * station, so just return; vehicle_id for this station is
2674      * SPRITE_INDEX_NULL. */
2675     return true;
2676 }
2677 
2678 /**
2679  * Checks whether a vehicle can depart a station when set to synchronise with adjacent stations.
2680  *  rct2: 0x006DE287
2681  * @param vehicle The vehicle waiting to depart.
2682  * @returns true if the vehicle can depart (all adjacent trains are ready or broken down), otherwise false.
2683  *
2684  * Permits vehicles to depart in two ways:
2685  *  Returns true, permitting the vehicle in the param to depart immediately;
2686  *  The vehicle flag VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT is cleared for those
2687  *  vehicles that depart in sync with the vehicle in the param.
2688  */
ride_station_can_depart_synchronised(const Ride & ride,StationIndex station)2689 static bool ride_station_can_depart_synchronised(const Ride& ride, StationIndex station)
2690 {
2691     auto location = ride.stations[station].GetStart();
2692 
2693     auto tileElement = map_get_track_element_at(location);
2694     if (tileElement == nullptr)
2695     {
2696         return false;
2697     }
2698 
2699     // Reset the list of synchronised vehicles to empty.
2700     _lastSynchronisedVehicle = _synchronisedVehicles;
2701 
2702     /* Search for stations to sync in both directions from the current tile.
2703      * We allow for some space between stations, and every time a station
2704      *  is found we allow for space between that and the next.
2705      */
2706 
2707     int32_t direction = tileElement->GetDirectionWithOffset(1);
2708     int32_t maxCheckDistance = RIDE_ADJACENCY_CHECK_DISTANCE;
2709     int32_t spaceBetween = maxCheckDistance;
2710 
2711     while (_lastSynchronisedVehicle < &_synchronisedVehicles[SYNCHRONISED_VEHICLE_COUNT - 1])
2712     {
2713         location += CoordsXYZ{ CoordsDirectionDelta[direction], 0 };
2714         if (try_add_synchronised_station(location))
2715         {
2716             spaceBetween = maxCheckDistance;
2717             continue;
2718         }
2719         if (spaceBetween-- == 0)
2720         {
2721             break;
2722         }
2723     }
2724 
2725     // Other search direction.
2726     location = ride.stations[station].GetStart();
2727     direction = direction_reverse(direction) & 3;
2728     spaceBetween = maxCheckDistance;
2729     while (_lastSynchronisedVehicle < &_synchronisedVehicles[SYNCHRONISED_VEHICLE_COUNT - 1])
2730     {
2731         location += CoordsXYZ{ CoordsDirectionDelta[direction], 0 };
2732         if (try_add_synchronised_station(location))
2733         {
2734             spaceBetween = maxCheckDistance;
2735             continue;
2736         }
2737         if (spaceBetween-- == 0)
2738         {
2739             break;
2740         }
2741     }
2742 
2743     if (_lastSynchronisedVehicle == _synchronisedVehicles)
2744     {
2745         // No adjacent stations, allow depart
2746         return true;
2747     }
2748 
2749     for (rct_synchronised_vehicle* sv = _synchronisedVehicles; sv < _lastSynchronisedVehicle; sv++)
2750     {
2751         Ride* sv_ride = get_ride(sv->ride_id);
2752 
2753         if (!(sv_ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN))
2754         {
2755             if (sv_ride->status != RideStatus::Closed)
2756             {
2757                 if (sv_ride->IsBlockSectioned())
2758                 {
2759                     if (!(sv_ride->stations[sv->stationIndex].Depart & STATION_DEPART_FLAG))
2760                     {
2761                         sv = _synchronisedVehicles;
2762                         ride_id_t rideId = RIDE_ID_NULL;
2763                         for (; sv < _lastSynchronisedVehicle; sv++)
2764                         {
2765                             if (rideId == RIDE_ID_NULL)
2766                             {
2767                                 rideId = sv->ride_id;
2768                             }
2769                             if (rideId != sv->ride_id)
2770                             {
2771                                 // Here the sync-ed stations are not all from the same ride.
2772                                 return false;
2773                             }
2774                         }
2775 
2776                         /* Here all the of sync-ed stations are from the same ride */
2777                         auto curRide = get_ride(rideId);
2778                         if (curRide != nullptr)
2779                         {
2780                             for (int32_t i = 0; i < curRide->num_vehicles; i++)
2781                             {
2782                                 Vehicle* v = GetEntity<Vehicle>(curRide->vehicles[i]);
2783                                 if (v == nullptr)
2784                                 {
2785                                     continue;
2786                                 }
2787                                 if (v->status != Vehicle::Status::WaitingToDepart && v->velocity != 0)
2788                                 {
2789                                     // Here at least one vehicle on the ride is moving.
2790                                     return false;
2791                                 }
2792                             }
2793                         }
2794 
2795                         // UNCERTAIN: is the return desired here, or rather continue on with the general checks?
2796                         return true;
2797                     }
2798                 }
2799                 // There is no vehicle waiting at this station to sync with.
2800                 if (sv->vehicle_id == SPRITE_INDEX_NULL)
2801                 {
2802                     // Check conditions for departing without all stations being in sync.
2803                     if (_lastSynchronisedVehicle > &_synchronisedVehicles[1])
2804                     {
2805                         // Sync condition: there are at least 3 stations to sync
2806                         return false;
2807                     }
2808                     ride_id_t someRideIndex = _synchronisedVehicles[0].ride_id;
2809                     if (someRideIndex != ride.id)
2810                     {
2811                         // Sync condition: the first station to sync is a different ride
2812                         return false;
2813                     }
2814 
2815                     int32_t numTrainsAtStation = 0;
2816                     int32_t numTravelingTrains = 0;
2817                     auto currentStation = sv->stationIndex;
2818                     for (int32_t i = 0; i < sv_ride->num_vehicles; i++)
2819                     {
2820                         auto* otherVehicle = GetEntity<Vehicle>(sv_ride->vehicles[i]);
2821                         if (otherVehicle == nullptr)
2822                         {
2823                             continue;
2824                         }
2825                         if (otherVehicle->status != Vehicle::Status::Travelling)
2826                         {
2827                             if (currentStation == otherVehicle->current_station)
2828                             {
2829                                 if (otherVehicle->status == Vehicle::Status::WaitingToDepart
2830                                     || otherVehicle->status == Vehicle::Status::MovingToEndOfStation)
2831                                 {
2832                                     numTrainsAtStation++;
2833                                 }
2834                             }
2835                         }
2836                         else
2837                         {
2838                             numTravelingTrains++;
2839                         }
2840                     }
2841 
2842                     int32_t totalTrains = numTrainsAtStation + numTravelingTrains;
2843                     if (totalTrains != sv_ride->num_vehicles || numTravelingTrains >= sv_ride->num_vehicles / 2)
2844                     {
2845                         // if (numArrivingTrains > 0 || numTravelingTrains >= sv_ride->num_vehicles / 2) {
2846                         /* Sync condition: If there are trains arriving at the
2847                          * station or half or more of the ride trains are
2848                          * travelling, this station must be sync-ed before the
2849                          * trains can depart! */
2850                         return false;
2851                     }
2852 
2853                     /* Sync exception - train is not arriving at the station
2854                      * and there are less than half the trains for the ride
2855                      * travelling. */
2856                     continue;
2857                 }
2858             }
2859         }
2860     }
2861 
2862     // At this point all vehicles in _snychronisedVehicles can depart.
2863     for (rct_synchronised_vehicle* sv = _synchronisedVehicles; sv < _lastSynchronisedVehicle; sv++)
2864     {
2865         auto v = GetEntity<Vehicle>(sv->vehicle_id);
2866         if (v != nullptr)
2867         {
2868             v->ClearUpdateFlag(VEHICLE_UPDATE_FLAG_WAIT_ON_ADJACENT);
2869         }
2870     }
2871 
2872     return true;
2873 }
2874 
CanDepartSynchronised() const2875 bool Vehicle::CanDepartSynchronised() const
2876 {
2877     auto curRide = GetRide();
2878     if (curRide == nullptr)
2879         return false;
2880     return ride_station_can_depart_synchronised(*curRide, current_station);
2881 }
2882 
2883 /**
2884  *
2885  *  rct2: 0x006D9EB0
2886  */
PeepEasterEggHereWeAre() const2887 void Vehicle::PeepEasterEggHereWeAre() const
2888 {
2889     for (Vehicle* vehicle = GetEntity<Vehicle>(sprite_index); vehicle != nullptr;
2890          vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
2891     {
2892         for (int32_t i = 0; i < vehicle->num_peeps; ++i)
2893         {
2894             auto* curPeep = GetEntity<Guest>(vehicle->peep[i]);
2895             if (curPeep != nullptr && curPeep->PeepFlags & PEEP_FLAGS_HERE_WE_ARE)
2896             {
2897                 curPeep->InsertNewThought(PeepThoughtType::HereWeAre, curPeep->CurrentRide);
2898             }
2899         }
2900     }
2901 }
2902 
2903 /**
2904  * Performed when vehicle has completed a full circuit
2905  *  rct2: 0x006D7338
2906  */
test_finish(Ride & ride)2907 static void test_finish(Ride& ride)
2908 {
2909     ride.lifecycle_flags &= ~RIDE_LIFECYCLE_TEST_IN_PROGRESS;
2910     ride.lifecycle_flags |= RIDE_LIFECYCLE_TESTED;
2911 
2912     for (int32_t i = ride.num_stations - 1; i >= 1; i--)
2913     {
2914         if (ride.stations[i - 1].SegmentTime != 0)
2915             continue;
2916 
2917         uint16_t oldTime = ride.stations[i - 1].SegmentTime;
2918         ride.stations[i - 1].SegmentTime = ride.stations[i].SegmentTime;
2919         ride.stations[i].SegmentTime = oldTime;
2920 
2921         int32_t oldLength = ride.stations[i - 1].SegmentLength;
2922         ride.stations[i - 1].SegmentLength = ride.stations[i].SegmentLength;
2923         ride.stations[i].SegmentLength = oldLength;
2924     }
2925 
2926     uint32_t totalTime = 0;
2927     for (uint8_t i = 0; i < ride.num_stations; ++i)
2928     {
2929         totalTime += ride.stations[i].SegmentTime;
2930     }
2931 
2932     totalTime = std::max(totalTime, 1u);
2933     ride.average_speed = ride.average_speed / totalTime;
2934     window_invalidate_by_number(WC_RIDE, EnumValue(ride.id));
2935 }
2936 
UpdateTestFinish()2937 void Vehicle::UpdateTestFinish()
2938 {
2939     auto curRide = GetRide();
2940     if (curRide == nullptr)
2941         return;
2942     test_finish(*curRide);
2943     ClearUpdateFlag(VEHICLE_UPDATE_FLAG_TESTING);
2944 }
2945 
2946 /**
2947  *
2948  *  rct2: 0x006D6BE7
2949  */
test_reset(Ride & ride,StationIndex curStation)2950 static void test_reset(Ride& ride, StationIndex curStation)
2951 {
2952     ride.lifecycle_flags |= RIDE_LIFECYCLE_TEST_IN_PROGRESS;
2953     ride.lifecycle_flags &= ~RIDE_LIFECYCLE_NO_RAW_STATS;
2954     ride.max_speed = 0;
2955     ride.average_speed = 0;
2956     ride.current_test_segment = 0;
2957     ride.average_speed_test_timeout = 0;
2958     ride.max_positive_vertical_g = FIXED_2DP(1, 0);
2959     ride.max_negative_vertical_g = FIXED_2DP(1, 0);
2960     ride.max_lateral_g = 0;
2961     ride.previous_vertical_g = 0;
2962     ride.previous_lateral_g = 0;
2963     ride.testing_flags = 0;
2964     ride.CurTestTrackLocation.SetNull();
2965     ride.turn_count_default = 0;
2966     ride.turn_count_banked = 0;
2967     ride.turn_count_sloped = 0;
2968     ride.inversions = 0;
2969     ride.holes = 0;
2970     ride.sheltered_eighths = 0;
2971     ride.drops = 0;
2972     ride.sheltered_length = 0;
2973     ride.var_11C = 0;
2974     ride.num_sheltered_sections = 0;
2975     ride.highest_drop_height = 0;
2976     ride.special_track_elements = 0;
2977     for (auto& station : ride.stations)
2978     {
2979         station.SegmentLength = 0;
2980         station.SegmentTime = 0;
2981     }
2982     ride.total_air_time = 0;
2983     ride.current_test_station = curStation;
2984     window_invalidate_by_number(WC_RIDE, EnumValue(ride.id));
2985 }
2986 
TestReset()2987 void Vehicle::TestReset()
2988 {
2989     SetUpdateFlag(VEHICLE_UPDATE_FLAG_TESTING);
2990     auto curRide = GetRide();
2991     if (curRide == nullptr)
2992         return;
2993     test_reset(*curRide, current_station);
2994 }
2995 
CurrentTowerElementIsTop()2996 bool Vehicle::CurrentTowerElementIsTop()
2997 {
2998     TileElement* tileElement = map_get_track_element_at_of_type(TrackLocation, GetTrackType());
2999     if (tileElement != nullptr)
3000     {
3001         while (!tileElement->IsLastForTile())
3002         {
3003             tileElement++;
3004             if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK
3005                 && tileElement->AsTrack()->GetTrackType() == TrackElemType::TowerSection)
3006             {
3007                 return false;
3008             }
3009         }
3010     }
3011     return true;
3012 }
3013 
3014 /**
3015  *
3016  *  rct2: 0x006D986C
3017  */
UpdateTravellingBoatHireSetup()3018 void Vehicle::UpdateTravellingBoatHireSetup()
3019 {
3020     var_34 = sprite_direction;
3021     TrackLocation.x = x;
3022     TrackLocation.y = y;
3023     TrackLocation = TrackLocation.ToTileStart();
3024 
3025     CoordsXY location = CoordsXY(TrackLocation) + CoordsDirectionDelta[sprite_direction >> 3];
3026 
3027     BoatLocation = location;
3028     var_35 = 0;
3029     // No longer on a track so reset to 0 for import/export
3030     SetTrackDirection(0);
3031     SetTrackType(0);
3032     SetState(Vehicle::Status::TravellingBoat);
3033     remaining_distance += 27924;
3034 
3035     UpdateTravellingBoat();
3036 }
3037 
3038 /**
3039  *
3040  *  rct2: 0x006D982F
3041  */
UpdateDepartingBoatHire()3042 void Vehicle::UpdateDepartingBoatHire()
3043 {
3044     lost_time_out = 0;
3045 
3046     auto curRide = GetRide();
3047     if (curRide == nullptr)
3048         return;
3049 
3050     curRide->stations[current_station].Depart &= STATION_DEPART_FLAG;
3051     uint8_t waitingTime = std::max(curRide->min_waiting_time, static_cast<uint8_t>(3));
3052     waitingTime = std::min(waitingTime, static_cast<uint8_t>(127));
3053     curRide->stations[current_station].Depart |= waitingTime;
3054     UpdateTravellingBoatHireSetup();
3055 }
3056 
3057 /**
3058  *
3059  *  rct2: 0x006D845B
3060  */
UpdateDeparting()3061 void Vehicle::UpdateDeparting()
3062 {
3063     auto curRide = GetRide();
3064     if (curRide == nullptr)
3065         return;
3066 
3067     auto rideEntry = GetRideEntry();
3068     if (rideEntry == nullptr)
3069         return;
3070 
3071     if (sub_state == 0)
3072     {
3073         if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_TRAIN))
3074         {
3075             if (curRide->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
3076                 return;
3077 
3078             curRide->lifecycle_flags |= RIDE_LIFECYCLE_BROKEN_DOWN;
3079             ride_breakdown_add_news_item(curRide);
3080 
3081             curRide->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST
3082                 | RIDE_INVALIDATE_RIDE_MAINTENANCE;
3083             curRide->mechanic_status = RIDE_MECHANIC_STATUS_CALLING;
3084             curRide->inspection_station = current_station;
3085             curRide->breakdown_reason = curRide->breakdown_reason_pending;
3086             velocity = 0;
3087             return;
3088         }
3089 
3090         sub_state = 1;
3091         PeepEasterEggHereWeAre();
3092 
3093         if (rideEntry->flags & RIDE_ENTRY_FLAG_PLAY_DEPART_SOUND)
3094         {
3095             auto soundId = (rideEntry->vehicles[0].sound_range == 4) ? OpenRCT2::Audio::SoundId::Tram
3096                                                                      : OpenRCT2::Audio::SoundId::TrainDeparting;
3097 
3098             OpenRCT2::Audio::Play3D(soundId, GetLocation());
3099         }
3100 
3101         if (curRide->mode == RideMode::UpwardLaunch || (curRide->mode == RideMode::DownwardLaunch && var_CE > 1))
3102         {
3103             OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::RideLaunch2, GetLocation());
3104         }
3105 
3106         if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_TESTED))
3107         {
3108             if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_TESTING))
3109             {
3110                 if (curRide->current_test_segment + 1 < curRide->num_stations)
3111                 {
3112                     curRide->current_test_segment++;
3113                     curRide->current_test_station = current_station;
3114                 }
3115                 else
3116                 {
3117                     UpdateTestFinish();
3118                 }
3119             }
3120             else if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS) && !IsGhost())
3121             {
3122                 TestReset();
3123             }
3124         }
3125     }
3126 
3127     rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle_type];
3128 
3129     switch (curRide->mode)
3130     {
3131         case RideMode::ReverseInclineLaunchedShuttle:
3132             if (velocity >= -131940)
3133                 acceleration = -3298;
3134             break;
3135         case RideMode::PoweredLaunchPasstrough:
3136         case RideMode::PoweredLaunch:
3137         case RideMode::PoweredLaunchBlockSectioned:
3138         case RideMode::LimPoweredLaunch:
3139         case RideMode::UpwardLaunch:
3140             if (curRide->type == RIDE_TYPE_AIR_POWERED_VERTICAL_COASTER)
3141             {
3142                 if ((curRide->launch_speed << 16) > velocity)
3143                 {
3144                     acceleration = curRide->launch_speed << 13;
3145                 }
3146                 break;
3147             }
3148 
3149             if ((curRide->launch_speed << 16) > velocity)
3150                 acceleration = curRide->launch_speed << 12;
3151             break;
3152         case RideMode::DownwardLaunch:
3153             if (var_CE >= 1)
3154             {
3155                 if ((14 << 16) > velocity)
3156                     acceleration = 14 << 12;
3157                 break;
3158             }
3159             [[fallthrough]];
3160         case RideMode::ContinuousCircuit:
3161         case RideMode::ContinuousCircuitBlockSectioned:
3162         case RideMode::RotatingLift:
3163         case RideMode::FreefallDrop:
3164         case RideMode::BoatHire:
3165             if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_POWERED)
3166                 break;
3167 
3168             if (velocity <= 131940)
3169                 acceleration = 3298;
3170             break;
3171         default:
3172         {
3173             // This is workaround for multiple compilation errors of type "enumeration value ‘RIDE_MODE_*' not handled
3174             // in switch [-Werror=switch]"
3175         }
3176     }
3177 
3178     uint32_t curFlags = UpdateTrackMotion(nullptr);
3179 
3180     if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_8)
3181     {
3182         if (curRide->mode == RideMode::ReverseInclineLaunchedShuttle)
3183         {
3184             velocity = -velocity;
3185             FinishDeparting();
3186             return;
3187         }
3188     }
3189 
3190     if (curFlags & (VEHICLE_UPDATE_MOTION_TRACK_FLAG_5 | VEHICLE_UPDATE_MOTION_TRACK_FLAG_12))
3191     {
3192         if (curRide->mode == RideMode::BoatHire)
3193         {
3194             UpdateDepartingBoatHire();
3195             return;
3196         }
3197         if (curRide->mode == RideMode::ReverseInclineLaunchedShuttle)
3198         {
3199             velocity = -velocity;
3200             FinishDeparting();
3201             return;
3202         }
3203         if (curRide->mode == RideMode::Shuttle)
3204         {
3205             update_flags ^= VEHICLE_UPDATE_FLAG_REVERSING_SHUTTLE;
3206             velocity = 0;
3207 
3208             // We have turned, so treat it like entering a new tile
3209             UpdateCrossings();
3210         }
3211     }
3212 
3213     if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_ON_LIFT_HILL)
3214     {
3215         sound2_flags |= VEHICLE_SOUND2_FLAGS_LIFT_HILL;
3216         if (curRide->mode != RideMode::ReverseInclineLaunchedShuttle)
3217         {
3218             int32_t curSpeed = curRide->lift_hill_speed * 31079;
3219             if (velocity <= curSpeed)
3220             {
3221                 acceleration = 15539;
3222                 if (velocity != 0)
3223                 {
3224                     if (_vehicleBreakdown == BREAKDOWN_SAFETY_CUT_OUT)
3225                     {
3226                         SetUpdateFlag(VEHICLE_UPDATE_FLAG_ZERO_VELOCITY);
3227                         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_COLLISION_DISABLED);
3228                     }
3229                 }
3230                 else
3231                     ClearUpdateFlag(VEHICLE_UPDATE_FLAG_COLLISION_DISABLED);
3232             }
3233         }
3234         else
3235         {
3236             int32_t curSpeed = curRide->lift_hill_speed * -31079;
3237             if (velocity >= curSpeed)
3238             {
3239                 acceleration = -15539;
3240                 if (velocity != 0)
3241                 {
3242                     if (_vehicleBreakdown == BREAKDOWN_SAFETY_CUT_OUT)
3243                     {
3244                         SetUpdateFlag(VEHICLE_UPDATE_FLAG_ZERO_VELOCITY);
3245                         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_COLLISION_DISABLED);
3246                     }
3247                 }
3248                 else
3249                     ClearUpdateFlag(VEHICLE_UPDATE_FLAG_COLLISION_DISABLED);
3250             }
3251         }
3252     }
3253 
3254     if (curRide->mode == RideMode::FreefallDrop)
3255     {
3256         animation_frame++;
3257     }
3258     else
3259     {
3260         bool shouldLaunch = true;
3261         if (curRide->mode == RideMode::DownwardLaunch)
3262         {
3263             if (var_CE < 1)
3264                 shouldLaunch = false;
3265         }
3266 
3267         if (shouldLaunch)
3268         {
3269             if (!(curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_3) || _vehicleStationIndex != current_station)
3270             {
3271                 FinishDeparting();
3272                 return;
3273             }
3274 
3275             if (!(curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_5))
3276                 return;
3277             if (curRide->mode == RideMode::BoatHire || curRide->mode == RideMode::RotatingLift
3278                 || curRide->mode == RideMode::Shuttle)
3279                 return;
3280 
3281             UpdateCrashSetup();
3282             return;
3283         }
3284     }
3285 
3286     if (!CurrentTowerElementIsTop())
3287     {
3288         if (curRide->mode == RideMode::FreefallDrop)
3289             Invalidate();
3290         return;
3291     }
3292 
3293     FinishDeparting();
3294 }
3295 
3296 /**
3297  * Part of UpdateDeparting
3298  * Called after finishing departing sequence to enter
3299  * traveling state.
3300  * Vertical rides class the lift to the top of the tower
3301  * as the departing sequence. After this point station
3302  * boosters do not affect the ride.
3303  *  rct2: 0x006D8858
3304  */
FinishDeparting()3305 void Vehicle::FinishDeparting()
3306 {
3307     auto curRide = GetRide();
3308     if (curRide == nullptr)
3309         return;
3310 
3311     if (curRide->mode == RideMode::DownwardLaunch)
3312     {
3313         if (var_CE >= 1 && (14 << 16) > velocity)
3314             return;
3315 
3316         OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::RideLaunch1, GetLocation());
3317     }
3318 
3319     if (curRide->mode == RideMode::UpwardLaunch)
3320     {
3321         if ((curRide->launch_speed << 16) > velocity)
3322             return;
3323 
3324         OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::RideLaunch1, GetLocation());
3325     }
3326 
3327     if (curRide->mode != RideMode::Race && !curRide->IsBlockSectioned())
3328     {
3329         curRide->stations[current_station].Depart &= STATION_DEPART_FLAG;
3330         uint8_t waitingTime = 3;
3331         if (curRide->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH)
3332         {
3333             waitingTime = std::max(curRide->min_waiting_time, static_cast<uint8_t>(3));
3334             waitingTime = std::min(waitingTime, static_cast<uint8_t>(127));
3335         }
3336 
3337         curRide->stations[current_station].Depart |= waitingTime;
3338     }
3339     lost_time_out = 0;
3340     SetState(Vehicle::Status::Travelling, 1);
3341     if (velocity < 0)
3342         sub_state = 0;
3343 }
3344 
3345 /**
3346  *
3347  *  rct2: 0x006DE5CB
3348  */
CheckIfMissing()3349 void Vehicle::CheckIfMissing()
3350 {
3351     auto curRide = GetRide();
3352     if (curRide == nullptr)
3353         return;
3354 
3355     if (curRide->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))
3356         return;
3357 
3358     if (curRide->IsBlockSectioned())
3359         return;
3360 
3361     if (!curRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_CHECK_FOR_STALLING))
3362         return;
3363 
3364     lost_time_out++;
3365     if (curRide->lifecycle_flags & RIDE_LIFECYCLE_HAS_STALLED_VEHICLE)
3366         return;
3367 
3368     uint16_t limit = curRide->type == RIDE_TYPE_BOAT_HIRE ? 15360 : 9600;
3369 
3370     if (lost_time_out <= limit)
3371         return;
3372 
3373     curRide->lifecycle_flags |= RIDE_LIFECYCLE_HAS_STALLED_VEHICLE;
3374 
3375     if (gConfigNotifications.ride_stalled_vehicles)
3376     {
3377         Formatter ft;
3378         ft.Add<rct_string_id>(GetRideComponentName(GetRideTypeDescriptor(curRide->type).NameConvention.vehicle).number);
3379 
3380         uint8_t vehicleIndex = 0;
3381         for (; vehicleIndex < curRide->num_vehicles; ++vehicleIndex)
3382             if (curRide->vehicles[vehicleIndex] == sprite_index)
3383                 break;
3384 
3385         vehicleIndex++;
3386         ft.Add<uint16_t>(vehicleIndex);
3387         curRide->FormatNameTo(ft);
3388         ft.Add<rct_string_id>(GetRideComponentName(GetRideTypeDescriptor(curRide->type).NameConvention.station).singular);
3389 
3390         News::AddItemToQueue(News::ItemType::Ride, STR_NEWS_VEHICLE_HAS_STALLED, EnumValue(ride), ft);
3391     }
3392 }
3393 
SimulateCrash() const3394 void Vehicle::SimulateCrash() const
3395 {
3396     auto curRide = GetRide();
3397     if (curRide != nullptr)
3398     {
3399         curRide->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED;
3400     }
3401 }
3402 
3403 /**
3404  * Setup function for a vehicle colliding with
3405  * another vehicle.
3406  *
3407  *  rct2: 0x006DA059
3408  */
UpdateCollisionSetup()3409 void Vehicle::UpdateCollisionSetup()
3410 {
3411     auto curRide = GetRide();
3412     if (curRide == nullptr)
3413         return;
3414 
3415     if (curRide->status == RideStatus::Simulating)
3416     {
3417         SimulateCrash();
3418         return;
3419     }
3420 
3421     SetState(Vehicle::Status::Crashed, sub_state);
3422 
3423     if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_CRASHED))
3424     {
3425         auto frontVehicle = GetHead();
3426         auto trainIndex = ride_get_train_index_from_vehicle(curRide, frontVehicle->sprite_index);
3427         if (!trainIndex.has_value())
3428         {
3429             return;
3430         }
3431 
3432         curRide->Crash(trainIndex.value());
3433 
3434         if (curRide->status != RideStatus::Closed)
3435         {
3436             // We require this to execute right away during the simulation, always ignore network and queue.
3437             auto gameAction = RideSetStatusAction(curRide->id, RideStatus::Closed);
3438             GameActions::ExecuteNested(&gameAction);
3439         }
3440     }
3441 
3442     curRide->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED;
3443     curRide->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
3444     KillAllPassengersInTrain();
3445 
3446     Vehicle* lastVehicle = this;
3447     for (Vehicle* train = GetEntity<Vehicle>(sprite_index); train != nullptr;
3448          train = GetEntity<Vehicle>(train->next_vehicle_on_train))
3449     {
3450         lastVehicle = train;
3451 
3452         train->sub_state = 2;
3453 
3454 #ifdef ENABLE_SCRIPTING
3455         InvokeVehicleCrashHook(train->sprite_index, "another_vehicle");
3456 #endif
3457         const auto trainLoc = train->GetLocation();
3458 
3459         OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Crash, trainLoc);
3460 
3461         ExplosionCloud::Create(trainLoc);
3462 
3463         for (int32_t i = 0; i < 10; i++)
3464         {
3465             VehicleCrashParticle::Create(train->colours, trainLoc);
3466         }
3467 
3468         train->IsCrashedVehicle = true;
3469         train->animationState = scenario_rand() & 0xFFFF;
3470 
3471         train->animation_frame = scenario_rand() & 0x7;
3472         train->sprite_width = 13;
3473         train->sprite_height_negative = 45;
3474         train->sprite_height_positive = 5;
3475 
3476         train->MoveTo(trainLoc);
3477 
3478         train->SwingSpeed = 0;
3479     }
3480 
3481     // Remove the current train from the ride linked list of trains
3482     auto prevTrain = GetEntity<Vehicle>(prev_vehicle_on_ride);
3483     auto nextTrain = GetEntity<Vehicle>(lastVehicle->next_vehicle_on_ride);
3484     if (prevTrain == nullptr || nextTrain == nullptr)
3485     {
3486         log_error("Corrupted vehicle list for ride!");
3487     }
3488     else
3489     {
3490         prevTrain->next_vehicle_on_ride = lastVehicle->next_vehicle_on_ride;
3491         nextTrain->prev_vehicle_on_ride = prev_vehicle_on_ride;
3492     }
3493 
3494     velocity = 0;
3495 }
3496 
3497 /** rct2: 0x009A3AC4, 0x009A3AC6 */
3498 static constexpr const CoordsXY stru_9A3AC4[] = {
3499     { -256, 0 }, { -236, 98 }, { -181, 181 }, { -98, 236 }, { 0, 256 },  { 98, 236 },   { 181, 181 },   { 236, 98 },
3500     { 256, 0 },  { 236, -98 }, { 181, -181 }, { 98, -236 }, { 0, -256 }, { -98, -236 }, { -181, -181 }, { -236, -98 },
3501 };
3502 
3503 /**
3504  *
3505  *  rct2: 0x006D9EFE
3506  */
UpdateCrashSetup()3507 void Vehicle::UpdateCrashSetup()
3508 {
3509     auto curRide = GetRide();
3510     if (curRide != nullptr && curRide->status == RideStatus::Simulating)
3511     {
3512         SimulateCrash();
3513         return;
3514     }
3515     SetState(Vehicle::Status::Crashing, sub_state);
3516 
3517     if (NumPeepsUntilTrainTail() != 0)
3518     {
3519         OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::HauntedHouseScream2, GetLocation());
3520     }
3521 
3522     int32_t edx = velocity >> 10;
3523 
3524     Vehicle* lastVehicle = this;
3525     uint16_t spriteId = sprite_index;
3526     for (Vehicle* trainVehicle; spriteId != SPRITE_INDEX_NULL; spriteId = trainVehicle->next_vehicle_on_train)
3527     {
3528         trainVehicle = GetEntity<Vehicle>(spriteId);
3529         if (trainVehicle == nullptr)
3530         {
3531             break;
3532         }
3533         lastVehicle = trainVehicle;
3534 
3535         trainVehicle->sub_state = 0;
3536         int32_t trainX = stru_9A3AC4[trainVehicle->sprite_direction / 2].x;
3537         int32_t trainY = stru_9A3AC4[trainVehicle->sprite_direction / 2].y;
3538         auto trainZ = Unk9A38D4[trainVehicle->Pitch] >> 23;
3539 
3540         int32_t ecx = Unk9A37E4[trainVehicle->Pitch] >> 15;
3541         trainX *= ecx;
3542         trainY *= ecx;
3543         trainX >>= 16;
3544         trainY >>= 16;
3545         trainX *= edx;
3546         trainY *= edx;
3547         trainZ *= edx;
3548         trainX >>= 8;
3549         trainY >>= 8;
3550         trainZ >>= 8;
3551 
3552         trainVehicle->crash_x = trainX;
3553         trainVehicle->crash_y = trainY;
3554         trainVehicle->crash_z = trainZ;
3555         trainVehicle->crash_x += (scenario_rand() & 0xF) - 8;
3556         trainVehicle->crash_y += (scenario_rand() & 0xF) - 8;
3557         trainVehicle->crash_z += (scenario_rand() & 0xF) - 8;
3558 
3559         trainVehicle->TrackLocation = { 0, 0, 0 };
3560     }
3561 
3562     // Remove the current train from the ride linked list of trains
3563     auto prevTrain = GetEntity<Vehicle>(prev_vehicle_on_ride);
3564     auto nextTrain = GetEntity<Vehicle>(lastVehicle->next_vehicle_on_ride);
3565     if (prevTrain == nullptr || nextTrain == nullptr)
3566     {
3567         log_error("Corrupted vehicle list for ride!");
3568     }
3569     else
3570     {
3571         prevTrain->next_vehicle_on_ride = lastVehicle->next_vehicle_on_ride;
3572         nextTrain->prev_vehicle_on_ride = prev_vehicle_on_ride;
3573     }
3574     velocity = 0;
3575 }
3576 
3577 /**
3578  *
3579  *  rct2: 0x006D8937
3580  */
UpdateTravelling()3581 void Vehicle::UpdateTravelling()
3582 {
3583     CheckIfMissing();
3584 
3585     auto curRide = GetRide();
3586     if (curRide == nullptr || (_vehicleBreakdown == 0 && curRide->mode == RideMode::RotatingLift))
3587         return;
3588 
3589     if (sub_state == 2)
3590     {
3591         velocity = 0;
3592         acceleration = 0;
3593         var_C0--;
3594         if (var_C0 == 0)
3595             sub_state = 0;
3596     }
3597 
3598     if (curRide->mode == RideMode::FreefallDrop && animation_frame != 0)
3599     {
3600         animation_frame++;
3601         velocity = 0;
3602         acceleration = 0;
3603         Invalidate();
3604         return;
3605     }
3606 
3607     uint32_t curFlags = UpdateTrackMotion(nullptr);
3608 
3609     bool skipCheck = false;
3610     if (curFlags & (VEHICLE_UPDATE_MOTION_TRACK_FLAG_8 | VEHICLE_UPDATE_MOTION_TRACK_FLAG_9)
3611         && curRide->mode == RideMode::ReverseInclineLaunchedShuttle && sub_state == 0)
3612     {
3613         sub_state = 1;
3614         velocity = 0;
3615         skipCheck = true;
3616     }
3617 
3618     if (!skipCheck)
3619     {
3620         if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_DERAILED)
3621         {
3622             UpdateCrashSetup();
3623             return;
3624         }
3625 
3626         if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION)
3627         {
3628             UpdateCollisionSetup();
3629             return;
3630         }
3631 
3632         if (curFlags & (VEHICLE_UPDATE_MOTION_TRACK_FLAG_5 | VEHICLE_UPDATE_MOTION_TRACK_FLAG_12))
3633         {
3634             if (curRide->mode == RideMode::RotatingLift)
3635             {
3636                 if (sub_state <= 1)
3637                 {
3638                     SetState(Vehicle::Status::Arriving, 1);
3639                     var_C0 = 0;
3640                     return;
3641                 }
3642             }
3643             else if (curRide->mode == RideMode::BoatHire)
3644             {
3645                 UpdateTravellingBoatHireSetup();
3646                 return;
3647             }
3648             if (curRide->mode == RideMode::Shuttle)
3649             {
3650                 update_flags ^= VEHICLE_UPDATE_FLAG_REVERSING_SHUTTLE;
3651                 velocity = 0;
3652             }
3653             else
3654             {
3655                 if (sub_state != 0)
3656                 {
3657                     UpdateCrashSetup();
3658                     return;
3659                 }
3660                 sub_state = 1;
3661                 velocity = 0;
3662             }
3663         }
3664     }
3665 
3666     if (curRide->mode == RideMode::RotatingLift && sub_state <= 1)
3667     {
3668         if (sub_state == 0)
3669         {
3670             if (velocity >= -131940)
3671                 acceleration = -3298;
3672             velocity = std::max(velocity, -131940);
3673         }
3674         else
3675         {
3676             if (CurrentTowerElementIsTop())
3677             {
3678                 velocity = 0;
3679                 sub_state = 2;
3680                 var_C0 = 150;
3681             }
3682             else
3683             {
3684                 if (velocity <= 131940)
3685                     acceleration = 3298;
3686             }
3687         }
3688     }
3689 
3690     if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_ON_LIFT_HILL)
3691     {
3692         if (curRide->mode == RideMode::ReverseInclineLaunchedShuttle)
3693         {
3694             if (sub_state == 0)
3695             {
3696                 if (velocity != 0)
3697                     sound2_flags |= VEHICLE_SOUND2_FLAGS_LIFT_HILL;
3698 
3699                 if (!HasUpdateFlag(VEHICLE_UPDATE_FLAG_12))
3700                 {
3701                     if (velocity >= curRide->lift_hill_speed * -31079)
3702                     {
3703                         acceleration = -15539;
3704 
3705                         if (_vehicleBreakdown == 0)
3706                         {
3707                             sound2_flags &= ~VEHICLE_SOUND2_FLAGS_LIFT_HILL;
3708                             SetUpdateFlag(VEHICLE_UPDATE_FLAG_ZERO_VELOCITY);
3709                         }
3710                     }
3711                 }
3712             }
3713         }
3714         else
3715         {
3716             sound2_flags |= VEHICLE_SOUND2_FLAGS_LIFT_HILL;
3717             if (velocity <= curRide->lift_hill_speed * 31079)
3718             {
3719                 acceleration = 15539;
3720                 if (velocity != 0)
3721                 {
3722                     if (_vehicleBreakdown == 0)
3723                     {
3724                         SetUpdateFlag(VEHICLE_UPDATE_FLAG_ZERO_VELOCITY);
3725                         sound2_flags &= ~VEHICLE_SOUND2_FLAGS_LIFT_HILL;
3726                     }
3727                 }
3728                 else
3729                 {
3730                     sound2_flags &= ~VEHICLE_SOUND2_FLAGS_LIFT_HILL;
3731                 }
3732             }
3733         }
3734     }
3735 
3736     if (!(curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_3))
3737         return;
3738 
3739     if (curRide->mode == RideMode::ReverseInclineLaunchedShuttle && velocity >= 0 && !HasUpdateFlag(VEHICLE_UPDATE_FLAG_12))
3740     {
3741         return;
3742     }
3743 
3744     if (curRide->mode == RideMode::PoweredLaunchPasstrough && velocity < 0)
3745         return;
3746 
3747     SetState(Vehicle::Status::Arriving);
3748     current_station = _vehicleStationIndex;
3749     var_C0 = 0;
3750     if (velocity < 0)
3751         sub_state = 1;
3752 }
3753 
UpdateArrivingPassThroughStation(const Ride & curRide,const rct_ride_entry_vehicle & vehicleEntry,bool stationBrakesWork)3754 void Vehicle::UpdateArrivingPassThroughStation(
3755     const Ride& curRide, const rct_ride_entry_vehicle& vehicleEntry, bool stationBrakesWork)
3756 {
3757     if (sub_state == 0)
3758     {
3759         if (curRide.mode == RideMode::Race && curRide.lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING)
3760         {
3761             return;
3762         }
3763 
3764         if (velocity <= 131940)
3765         {
3766             acceleration = 3298;
3767             return;
3768         }
3769 
3770         int32_t velocity_diff = velocity;
3771         if (velocity_diff >= 1572864)
3772             velocity_diff /= 8;
3773         else
3774             velocity_diff /= 16;
3775 
3776         if (!stationBrakesWork)
3777         {
3778             return;
3779         }
3780 
3781         if (curRide.num_circuits != 1)
3782         {
3783             if (num_laps + 1 < curRide.num_circuits)
3784             {
3785                 return;
3786             }
3787         }
3788         velocity -= velocity_diff;
3789         acceleration = 0;
3790     }
3791     else
3792     {
3793         if (!(vehicleEntry.flags & VEHICLE_ENTRY_FLAG_POWERED) && velocity >= -131940)
3794         {
3795             acceleration = -3298;
3796         }
3797 
3798         if (velocity >= -131940)
3799         {
3800             return;
3801         }
3802 
3803         int32_t velocity_diff = velocity;
3804         if (velocity_diff < -1572864)
3805             velocity_diff /= 8;
3806         else
3807             velocity_diff /= 16;
3808 
3809         if (!stationBrakesWork)
3810         {
3811             return;
3812         }
3813 
3814         if (num_laps + 1 < curRide.num_circuits)
3815         {
3816             return;
3817         }
3818 
3819         if (num_laps + 1 != curRide.num_circuits)
3820         {
3821             velocity -= velocity_diff;
3822             acceleration = 0;
3823             return;
3824         }
3825 
3826         if (GetRideTypeDescriptor(curRide.type).HasFlag(RIDE_TYPE_FLAG_ALLOW_MULTIPLE_CIRCUITS)
3827             && curRide.mode != RideMode::Shuttle && curRide.mode != RideMode::PoweredLaunch)
3828         {
3829             SetUpdateFlag(VEHICLE_UPDATE_FLAG_12);
3830         }
3831         else
3832         {
3833             velocity -= velocity_diff;
3834             acceleration = 0;
3835         }
3836     }
3837 }
3838 
3839 /**
3840  *
3841  *  rct2: 0x006D8C36
3842  */
UpdateArriving()3843 void Vehicle::UpdateArriving()
3844 {
3845     auto curRide = GetRide();
3846     if (curRide == nullptr)
3847         return;
3848 
3849     bool stationBrakesWork = true;
3850     uint32_t curFlags = 0;
3851 
3852     switch (curRide->mode)
3853     {
3854         case RideMode::Swing:
3855         case RideMode::Rotation:
3856         case RideMode::ForwardRotation:
3857         case RideMode::BackwardRotation:
3858         case RideMode::FilmAvengingAviators:
3859         case RideMode::FilmThrillRiders:
3860         case RideMode::Beginners:
3861         case RideMode::Intense:
3862         case RideMode::Berserk:
3863         case RideMode::MouseTails3DFilm:
3864         case RideMode::StormChasers3DFilm:
3865         case RideMode::SpaceRaiders3DFilm:
3866         case RideMode::Circus:
3867         case RideMode::SpaceRings:
3868         case RideMode::HauntedHouse:
3869         case RideMode::CrookedHouse:
3870             ClearUpdateFlag(VEHICLE_UPDATE_FLAG_12);
3871             velocity = 0;
3872             acceleration = 0;
3873             SetState(Vehicle::Status::UnloadingPassengers);
3874             return;
3875         default:
3876         {
3877             // This is workaround for multiple compilation errors of type "enumeration value ‘RIDE_MODE_*' not handled
3878             // in switch [-Werror=switch]"
3879         }
3880     }
3881 
3882     bool hasBrakesFailure = curRide->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN
3883         && curRide->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE;
3884     if (hasBrakesFailure && curRide->inspection_station == current_station
3885         && curRide->mechanic_status != RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES)
3886     {
3887         stationBrakesWork = false;
3888     }
3889 
3890     rct_ride_entry* rideEntry = GetRideEntry();
3891     rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle_type];
3892 
3893     UpdateArrivingPassThroughStation(*curRide, *vehicleEntry, stationBrakesWork);
3894 
3895     curFlags = UpdateTrackMotion(nullptr);
3896     if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION && !stationBrakesWork)
3897     {
3898         UpdateCollisionSetup();
3899         return;
3900     }
3901 
3902     if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION && !stationBrakesWork)
3903     {
3904         SetState(Vehicle::Status::Departing, 1);
3905         return;
3906     }
3907 
3908     if (!(curFlags
3909           & (VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION | VEHICLE_UPDATE_MOTION_TRACK_FLAG_1
3910              | VEHICLE_UPDATE_MOTION_TRACK_FLAG_5)))
3911     {
3912         if (velocity > 98955)
3913             var_C0 = 0;
3914         return;
3915     }
3916 
3917     var_C0++;
3918     if ((curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_1) && (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_GO_KART) && (var_C0 < 40))
3919     {
3920         return;
3921     }
3922 
3923     auto trackElement = map_get_track_element_at(TrackLocation);
3924 
3925     if (trackElement == nullptr)
3926     {
3927         return;
3928     }
3929 
3930     current_station = trackElement->GetStationIndex();
3931     num_laps++;
3932 
3933     if (sub_state != 0)
3934     {
3935         if (num_laps < curRide->num_circuits)
3936         {
3937             SetState(Vehicle::Status::Departing, 1);
3938             return;
3939         }
3940 
3941         if (num_laps == curRide->num_circuits && HasUpdateFlag(VEHICLE_UPDATE_FLAG_12))
3942         {
3943             SetState(Vehicle::Status::Departing, 1);
3944             return;
3945         }
3946     }
3947 
3948     if (curRide->num_circuits != 1 && num_laps < curRide->num_circuits)
3949     {
3950         SetState(Vehicle::Status::Departing, 1);
3951         return;
3952     }
3953 
3954     if ((curRide->mode == RideMode::UpwardLaunch || curRide->mode == RideMode::DownwardLaunch) && var_CE < 2)
3955     {
3956         OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::RideLaunch2, GetLocation());
3957         velocity = 0;
3958         acceleration = 0;
3959         SetState(Vehicle::Status::Departing, 1);
3960         return;
3961     }
3962 
3963     if (curRide->mode == RideMode::Race && curRide->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING)
3964     {
3965         SetState(Vehicle::Status::Departing, 1);
3966         return;
3967     }
3968 
3969     ClearUpdateFlag(VEHICLE_UPDATE_FLAG_12);
3970     velocity = 0;
3971     acceleration = 0;
3972     SetState(Vehicle::Status::UnloadingPassengers);
3973 }
3974 
3975 /**
3976  *
3977  *  rct2: 0x006D9002
3978  */
UpdateUnloadingPassengers()3979 void Vehicle::UpdateUnloadingPassengers()
3980 {
3981     if (sub_state == 0)
3982     {
3983         if (OpenRestraints())
3984         {
3985             sub_state = 1;
3986         }
3987     }
3988 
3989     auto curRide = GetRide();
3990     if (curRide == nullptr)
3991         return;
3992 
3993     if (curRide->mode == RideMode::ForwardRotation || curRide->mode == RideMode::BackwardRotation)
3994     {
3995         uint8_t seat = ((-Pitch) >> 3) & 0xF;
3996         if (restraints_position == 255 && (peep[seat * 2] != SPRITE_INDEX_NULL))
3997         {
3998             next_free_seat -= 2;
3999 
4000             auto firstGuest = GetEntity<Guest>(peep[seat * 2]);
4001             peep[seat * 2] = SPRITE_INDEX_NULL;
4002 
4003             if (firstGuest != nullptr)
4004             {
4005                 firstGuest->SetState(PeepState::LeavingRide);
4006                 firstGuest->RideSubState = PeepRideSubState::LeaveVehicle;
4007             }
4008 
4009             auto secondGuest = GetEntity<Guest>(peep[seat * 2 + 1]);
4010             peep[seat * 2 + 1] = SPRITE_INDEX_NULL;
4011 
4012             if (secondGuest != nullptr)
4013             {
4014                 secondGuest->SetState(PeepState::LeavingRide);
4015                 secondGuest->RideSubState = PeepRideSubState::LeaveVehicle;
4016             }
4017         }
4018     }
4019     else
4020     {
4021         if (ride_get_exit_location(curRide, current_station).IsNull())
4022         {
4023             if (sub_state != 1)
4024                 return;
4025 
4026             if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_TESTED) && HasUpdateFlag(VEHICLE_UPDATE_FLAG_TESTING)
4027                 && curRide->current_test_segment + 1 >= curRide->num_stations)
4028             {
4029                 UpdateTestFinish();
4030             }
4031             SetState(Vehicle::Status::MovingToEndOfStation);
4032             return;
4033         }
4034 
4035         for (Vehicle* train = GetEntity<Vehicle>(sprite_index); train != nullptr;
4036              train = GetEntity<Vehicle>(train->next_vehicle_on_train))
4037         {
4038             if (train->restraints_position != 255)
4039                 continue;
4040 
4041             if (train->next_free_seat == 0)
4042                 continue;
4043 
4044             train->next_free_seat = 0;
4045             for (uint8_t peepIndex = 0; peepIndex < train->num_peeps; peepIndex++)
4046             {
4047                 Peep* curPeep = GetEntity<Guest>(train->peep[peepIndex]);
4048                 if (curPeep != nullptr)
4049                 {
4050                     curPeep->SetState(PeepState::LeavingRide);
4051                     curPeep->RideSubState = PeepRideSubState::LeaveVehicle;
4052                 }
4053             }
4054         }
4055     }
4056 
4057     if (sub_state != 1)
4058         return;
4059 
4060     for (Vehicle* train = GetEntity<Vehicle>(sprite_index); train != nullptr;
4061          train = GetEntity<Vehicle>(train->next_vehicle_on_train))
4062     {
4063         if (train->num_peeps != train->next_free_seat)
4064             return;
4065     }
4066 
4067     if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_TESTED) && HasUpdateFlag(VEHICLE_UPDATE_FLAG_TESTING)
4068         && curRide->current_test_segment + 1 >= curRide->num_stations)
4069     {
4070         UpdateTestFinish();
4071     }
4072     SetState(Vehicle::Status::MovingToEndOfStation);
4073 }
4074 
4075 /**
4076  *
4077  *  rct2: 0x006D9CE9
4078  */
UpdateWaitingForCableLift()4079 void Vehicle::UpdateWaitingForCableLift()
4080 {
4081     auto curRide = GetRide();
4082     if (curRide == nullptr)
4083         return;
4084 
4085     Vehicle* cableLift = GetEntity<Vehicle>(curRide->cable_lift);
4086     if (cableLift == nullptr)
4087         return;
4088 
4089     if (cableLift->status != Vehicle::Status::WaitingForPassengers)
4090         return;
4091 
4092     cableLift->SetState(Vehicle::Status::WaitingToDepart, sub_state);
4093     cableLift->cable_lift_target = sprite_index;
4094 }
4095 
4096 /**
4097  *
4098  *  rct2: 0x006D9D21
4099  */
UpdateTravellingCableLift()4100 void Vehicle::UpdateTravellingCableLift()
4101 {
4102     auto curRide = GetRide();
4103     if (curRide == nullptr)
4104         return;
4105 
4106     if (sub_state == 0)
4107     {
4108         if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_TRAIN))
4109         {
4110             if (curRide->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN)
4111                 return;
4112 
4113             curRide->lifecycle_flags |= RIDE_LIFECYCLE_BROKEN_DOWN;
4114             ride_breakdown_add_news_item(curRide);
4115             curRide->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST
4116                 | RIDE_INVALIDATE_RIDE_MAINTENANCE;
4117 
4118             curRide->mechanic_status = RIDE_MECHANIC_STATUS_CALLING;
4119             curRide->inspection_station = current_station;
4120             curRide->breakdown_reason = curRide->breakdown_reason_pending;
4121             velocity = 0;
4122             return;
4123         }
4124 
4125         sub_state = 1;
4126         PeepEasterEggHereWeAre();
4127         if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_TESTED))
4128         {
4129             if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_TESTING))
4130             {
4131                 if (curRide->current_test_segment + 1 < curRide->num_stations)
4132                 {
4133                     curRide->current_test_segment++;
4134                     curRide->current_test_station = current_station;
4135                 }
4136                 else
4137                 {
4138                     UpdateTestFinish();
4139                 }
4140             }
4141             else if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS) && !IsGhost())
4142             {
4143                 TestReset();
4144             }
4145         }
4146     }
4147 
4148     if (velocity <= 439800)
4149     {
4150         acceleration = 4398;
4151     }
4152     int32_t curFlags = UpdateTrackMotion(nullptr);
4153 
4154     if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_11)
4155     {
4156         SetState(Vehicle::Status::Travelling, 1);
4157         lost_time_out = 0;
4158         return;
4159     }
4160 
4161     if (sub_state == 2)
4162         return;
4163 
4164     if (curFlags & VEHICLE_UPDATE_MOTION_TRACK_FLAG_3 && current_station == _vehicleStationIndex)
4165         return;
4166 
4167     sub_state = 2;
4168 
4169     if (curRide->IsBlockSectioned())
4170         return;
4171 
4172     // This is slightly different to the vanilla function
4173     curRide->stations[current_station].Depart &= STATION_DEPART_FLAG;
4174     uint8_t waitingTime = 3;
4175     if (curRide->depart_flags & RIDE_DEPART_WAIT_FOR_MINIMUM_LENGTH)
4176     {
4177         waitingTime = std::max(curRide->min_waiting_time, static_cast<uint8_t>(3));
4178         waitingTime = std::min(waitingTime, static_cast<uint8_t>(127));
4179     }
4180 
4181     curRide->stations[current_station].Depart |= waitingTime;
4182 }
4183 
4184 /**
4185  *
4186  *  rct2: 0x006D9820
4187  */
UpdateTravellingBoat()4188 void Vehicle::UpdateTravellingBoat()
4189 {
4190     CheckIfMissing();
4191     UpdateMotionBoatHire();
4192 }
4193 
TryReconnectBoatToTrack(const CoordsXY & currentBoatLocation,const CoordsXY & trackCoords)4194 void Vehicle::TryReconnectBoatToTrack(const CoordsXY& currentBoatLocation, const CoordsXY& trackCoords)
4195 {
4196     remaining_distance = 0;
4197     if (!UpdateMotionCollisionDetection({ currentBoatLocation, z }, nullptr))
4198     {
4199         TrackLocation.x = trackCoords.x;
4200         TrackLocation.y = trackCoords.y;
4201 
4202         auto trackElement = map_get_track_element_at(TrackLocation);
4203 
4204         auto curRide = GetRide();
4205         if (curRide != nullptr)
4206         {
4207             SetTrackType(trackElement->GetTrackType());
4208             SetTrackDirection(curRide->boat_hire_return_direction);
4209             BoatLocation.SetNull();
4210         }
4211 
4212         track_progress = 0;
4213         SetState(Vehicle::Status::Travelling, sub_state);
4214         unk_F64E20.x = currentBoatLocation.x;
4215         unk_F64E20.y = currentBoatLocation.y;
4216     }
4217 }
4218 
4219 /**
4220  *
4221  *  rct2: 0x006DA717
4222  */
UpdateMotionBoatHire()4223 void Vehicle::UpdateMotionBoatHire()
4224 {
4225     _vehicleMotionTrackFlags = 0;
4226     velocity += acceleration;
4227     _vehicleVelocityF64E08 = velocity;
4228     _vehicleVelocityF64E0C = (velocity >> 10) * 42;
4229 
4230     auto vehicleEntry = Entry();
4231     if (vehicleEntry == nullptr)
4232     {
4233         return;
4234     }
4235     if (vehicleEntry->flags & (VEHICLE_ENTRY_FLAG_VEHICLE_ANIMATION | VEHICLE_ENTRY_FLAG_RIDER_ANIMATION))
4236     {
4237         UpdateAdditionalAnimation();
4238     }
4239 
4240     _vehicleUnkF64E10 = 1;
4241     acceleration = 0;
4242     remaining_distance += _vehicleVelocityF64E0C;
4243     if (remaining_distance >= 0x368A)
4244     {
4245         sound2_flags &= ~VEHICLE_SOUND2_FLAGS_LIFT_HILL;
4246         unk_F64E20 = GetLocation();
4247         Invalidate();
4248 
4249         for (;;)
4250         {
4251             // loc_6DA7A5
4252             var_35++;
4253             auto loc = BoatLocation.ToTileCentre();
4254             CoordsXY loc2 = loc;
4255             uint8_t bl;
4256 
4257             loc2.x -= x;
4258             if (loc2.x >= 0)
4259             {
4260                 loc2.y -= y;
4261                 if (loc2.y < 0)
4262                 {
4263                     // loc_6DA81A:
4264                     loc2.y = -loc2.y;
4265                     bl = 24;
4266                     if (loc2.y <= loc2.x * 4)
4267                     {
4268                         bl = 16;
4269                         if (loc2.x <= loc2.y * 4)
4270                         {
4271                             bl = 20;
4272                         }
4273                     }
4274                 }
4275                 else
4276                 {
4277                     bl = 8;
4278                     if (loc2.y <= loc2.x * 4)
4279                     {
4280                         bl = 16;
4281                         if (loc2.x <= loc2.y * 4)
4282                         {
4283                             bl = 12;
4284                         }
4285                     }
4286                 }
4287             }
4288             else
4289             {
4290                 loc2.y -= y;
4291                 if (loc2.y < 0)
4292                 {
4293                     // loc_6DA83D:
4294                     loc2.x = -loc2.x;
4295                     loc2.y = -loc2.y;
4296                     bl = 24;
4297                     if (loc2.y <= loc2.x * 4)
4298                     {
4299                         bl = 0;
4300                         if (loc2.x <= loc2.y * 4)
4301                         {
4302                             bl = 28;
4303                         }
4304                     }
4305                 }
4306                 else
4307                 {
4308                     loc2.x = -loc2.x;
4309                     bl = 8;
4310                     if (loc2.y <= loc2.x * 4)
4311                     {
4312                         bl = 0;
4313                         if (loc2.x <= loc2.y * 4)
4314                         {
4315                             bl = 4;
4316                         }
4317                     }
4318                 }
4319             }
4320 
4321             // loc_6DA861:
4322             var_34 = bl;
4323             loc2.x += loc2.y;
4324             if (loc2.x <= 12)
4325             {
4326                 UpdateBoatLocation();
4327             }
4328 
4329             if (!(var_35 & (1 << 0)))
4330             {
4331                 uint8_t spriteDirection = sprite_direction;
4332                 if (spriteDirection != var_34)
4333                 {
4334                     uint8_t dl = (var_34 + 16 - spriteDirection) & 0x1E;
4335                     if (dl >= 16)
4336                     {
4337                         spriteDirection += 2;
4338                         if (dl > 24)
4339                         {
4340                             var_35--;
4341                         }
4342                     }
4343                     else
4344                     {
4345                         spriteDirection -= 2;
4346                         if (dl < 8)
4347                         {
4348                             var_35--;
4349                         }
4350                     }
4351 
4352                     sprite_direction = spriteDirection & 0x1E;
4353                 }
4354             }
4355 
4356             int32_t edi = (sprite_direction | (var_35 & 1)) & 0x1F;
4357             loc2 = { x + Unk9A36C4[edi].x, y + Unk9A36C4[edi].y };
4358             if (UpdateMotionCollisionDetection({ loc2, z }, nullptr))
4359             {
4360                 remaining_distance = 0;
4361                 if (sprite_direction == var_34)
4362                 {
4363                     sprite_direction ^= (1 << 4);
4364                     UpdateBoatLocation();
4365                     sprite_direction ^= (1 << 4);
4366                 }
4367                 break;
4368             }
4369 
4370             auto flooredLocation = loc2.ToTileStart();
4371             if (flooredLocation != TrackLocation)
4372             {
4373                 if (!vehicle_boat_is_location_accessible({ loc2, TrackLocation.z }))
4374                 {
4375                     // loc_6DA939:
4376                     auto curRide = GetRide();
4377                     if (curRide == nullptr)
4378                         return;
4379 
4380                     bool do_loc_6DAA97 = false;
4381                     if (sub_state != 1)
4382                     {
4383                         do_loc_6DAA97 = true;
4384                     }
4385                     else
4386                     {
4387                         auto flooredTileLoc = TileCoordsXY(flooredLocation);
4388                         if (curRide->boat_hire_return_position != flooredTileLoc)
4389                         {
4390                             do_loc_6DAA97 = true;
4391                         }
4392                     }
4393 
4394                     // loc_6DAA97:
4395                     if (do_loc_6DAA97)
4396                     {
4397                         remaining_distance = 0;
4398                         if (sprite_direction == var_34)
4399                         {
4400                             UpdateBoatLocation();
4401                         }
4402                         break;
4403                     }
4404 
4405                     if (!(curRide->boat_hire_return_direction & 1))
4406                     {
4407                         uint16_t tilePart = loc2.y % COORDS_XY_STEP;
4408                         if (tilePart == COORDS_XY_HALF_TILE)
4409                         {
4410                             TryReconnectBoatToTrack(loc2, flooredLocation);
4411                             break;
4412                         }
4413                         loc2 = unk_F64E20;
4414                         if (tilePart <= COORDS_XY_HALF_TILE)
4415                         {
4416                             loc2.y += 1;
4417                         }
4418                         else
4419                         {
4420                             loc2.y -= 1;
4421                         }
4422                     }
4423                     else
4424                     {
4425                         // loc_6DA9A2:
4426                         uint16_t tilePart = loc2.x % COORDS_XY_STEP;
4427                         if (tilePart == COORDS_XY_HALF_TILE)
4428                         {
4429                             TryReconnectBoatToTrack(loc2, flooredLocation);
4430                             break;
4431                         }
4432                         loc2 = unk_F64E20;
4433                         if (tilePart <= COORDS_XY_HALF_TILE)
4434                         {
4435                             loc2.x += 1;
4436                         }
4437                         else
4438                         {
4439                             loc2.x -= 1;
4440                         }
4441                     }
4442 
4443                     // loc_6DA9D1:
4444                     remaining_distance = 0;
4445                     if (!UpdateMotionCollisionDetection({ loc2, z }, nullptr))
4446                     {
4447                         unk_F64E20.x = loc2.x;
4448                         unk_F64E20.y = loc2.y;
4449                     }
4450                     break;
4451                 }
4452                 TrackLocation = { flooredLocation, TrackLocation.z };
4453             }
4454 
4455             remaining_distance -= Unk9A36C4[edi].distance;
4456             unk_F64E20.x = loc2.x;
4457             unk_F64E20.y = loc2.y;
4458             if (remaining_distance < 0x368A)
4459             {
4460                 break;
4461             }
4462             _vehicleUnkF64E10++;
4463         }
4464 
4465         MoveTo(unk_F64E20);
4466     }
4467 
4468     // loc_6DAAC9:
4469     {
4470         int32_t edx = velocity >> 8;
4471         edx = (edx * edx);
4472         if (velocity < 0)
4473         {
4474             edx = -edx;
4475         }
4476         edx >>= 5;
4477 
4478         // Hack to fix people messing with boat hire
4479         int32_t curMass = mass == 0 ? 1 : mass;
4480 
4481         int32_t eax = ((velocity >> 1) + edx) / curMass;
4482         int32_t ecx = -eax;
4483         if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_POWERED)
4484         {
4485             eax = speed << 14;
4486             int32_t ebx = (speed * curMass) >> 2;
4487             if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_REVERSING_SHUTTLE))
4488             {
4489                 eax = -eax;
4490             }
4491             eax -= velocity;
4492             edx = powered_acceleration * 2;
4493             ecx += (eax * edx) / ebx;
4494         }
4495         acceleration = ecx;
4496     }
4497     // eax = _vehicleMotionTrackFlags;
4498     // ebx = _vehicleStationIndex;
4499 }
4500 
4501 /**
4502  *
4503  *  rct2: 0x006DA280
4504  */
UpdateBoatLocation()4505 void Vehicle::UpdateBoatLocation()
4506 {
4507     auto curRide = GetRide();
4508     if (curRide == nullptr)
4509         return;
4510 
4511     TileCoordsXY returnPosition = curRide->boat_hire_return_position;
4512     uint8_t returnDirection = curRide->boat_hire_return_direction & 3;
4513 
4514     CoordsXY location = CoordsXY{ x, y } + CoordsDirectionDelta[returnDirection];
4515 
4516     if (location.ToTileStart() == returnPosition.ToCoordsXY())
4517     {
4518         sub_state = 1;
4519         BoatLocation = location.ToTileStart();
4520         return;
4521     }
4522 
4523     sub_state = 0;
4524     uint8_t curDirection = ((sprite_direction + 19) >> 3) & 3;
4525     uint8_t randDirection = scenario_rand() & 3;
4526 
4527     if (lost_time_out > 1920)
4528     {
4529         if (scenario_rand() & 1)
4530         {
4531             CoordsXY destLocation = (returnPosition.ToCoordsXY() - CoordsDirectionDelta[returnDirection]).ToTileCentre();
4532 
4533             destLocation.x -= x;
4534             destLocation.y -= y;
4535 
4536             if (abs(destLocation.x) <= abs(destLocation.y))
4537             {
4538                 randDirection = destLocation.y < 0 ? 3 : 1;
4539             }
4540             else
4541             {
4542                 randDirection = destLocation.x < 0 ? 0 : 2;
4543             }
4544         }
4545     }
4546 
4547     static constexpr const int8_t rotations[] = { 0, 1, -1, 2 };
4548     for (auto rotation : rotations)
4549     {
4550         if (randDirection + rotation == curDirection)
4551         {
4552             continue;
4553         }
4554 
4555         auto trackLocation = TrackLocation;
4556         trackLocation += CoordsDirectionDelta[(randDirection + rotation) & 3];
4557 
4558         if (!vehicle_boat_is_location_accessible(trackLocation))
4559         {
4560             continue;
4561         }
4562 
4563         BoatLocation = trackLocation.ToTileStart();
4564         return;
4565     }
4566 
4567     CoordsXY trackLocation = TrackLocation;
4568     trackLocation += CoordsDirectionDelta[curDirection & 3];
4569     BoatLocation = trackLocation.ToTileStart();
4570 }
4571 
4572 /**
4573  *
4574  *  rct2: 0x006DA22A
4575  */
vehicle_boat_is_location_accessible(const CoordsXYZ & location)4576 static bool vehicle_boat_is_location_accessible(const CoordsXYZ& location)
4577 {
4578     TileElement* tileElement = map_get_first_element_at(location);
4579     if (tileElement == nullptr)
4580         return false;
4581     do
4582     {
4583         if (tileElement->IsGhost())
4584             continue;
4585 
4586         if (tileElement->GetType() == TILE_ELEMENT_TYPE_SURFACE)
4587         {
4588             int32_t waterZ = tileElement->AsSurface()->GetWaterHeight();
4589             if (location.z != waterZ)
4590             {
4591                 return false;
4592             }
4593         }
4594         else
4595         {
4596             if (location.z > (tileElement->GetBaseZ() - (2 * COORDS_Z_STEP))
4597                 && location.z < tileElement->GetClearanceZ() + (2 * COORDS_Z_STEP))
4598             {
4599                 return false;
4600             }
4601         }
4602     } while (!(tileElement++)->IsLastForTile());
4603     return true;
4604 }
4605 
4606 /**
4607  *
4608  *  rct2: 0x006D9249
4609  */
UpdateSwinging()4610 void Vehicle::UpdateSwinging()
4611 {
4612     auto curRide = GetRide();
4613     if (curRide == nullptr)
4614         return;
4615 
4616     auto rideEntry = GetRideEntry();
4617     if (rideEntry == nullptr)
4618         return;
4619 
4620     // SubState for this ride means swinging state
4621     // 0 == first swing
4622     // 3 == full swing
4623     uint8_t swingState = sub_state;
4624     if (rideEntry->flags & RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_1)
4625     {
4626         swingState += 4;
4627         if (rideEntry->flags & RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_2)
4628             swingState += 4;
4629     }
4630 
4631     const int8_t* spriteMap = SwingingTimeToSpriteMaps[swingState];
4632     int8_t spriteType = spriteMap[current_time + 1];
4633 
4634     // 0x80 indicates that a complete swing has been
4635     // completed and the next swing can start
4636     if (spriteType != -128)
4637     {
4638         current_time++;
4639         if (static_cast<uint8_t>(spriteType) != Pitch)
4640         {
4641             // Used to know which sprite to draw
4642             Pitch = static_cast<uint8_t>(spriteType);
4643             Invalidate();
4644         }
4645         return;
4646     }
4647 
4648     current_time = -1;
4649     var_CE++;
4650     if (curRide->status != RideStatus::Closed)
4651     {
4652         // It takes 3 swings to get into full swing
4653         // ride->rotations already takes this into account
4654         if (var_CE + 3 < curRide->rotations)
4655         {
4656             // Go to the next swing state until we
4657             // are at full swing.
4658             if (sub_state != 3)
4659             {
4660                 sub_state++;
4661             }
4662             UpdateSwinging();
4663             return;
4664         }
4665     }
4666 
4667     // To get to this part of the code the
4668     // swing has to be in slowing down phase
4669     if (sub_state == 0)
4670     {
4671         SetState(Vehicle::Status::Arriving);
4672         var_C0 = 0;
4673         return;
4674     }
4675     // Go towards first swing state
4676     sub_state--;
4677     UpdateSwinging();
4678 }
4679 
4680 /**
4681  *
4682  *  rct2: 0x006D9413
4683  */
UpdateFerrisWheelRotating()4684 void Vehicle::UpdateFerrisWheelRotating()
4685 {
4686     if (_vehicleBreakdown == 0)
4687         return;
4688 
4689     auto curRide = GetRide();
4690     if (curRide == nullptr)
4691         return;
4692 
4693     if ((ferris_wheel_var_1 -= 1) != 0)
4694         return;
4695 
4696     int8_t curFerrisWheelVar0 = ferris_wheel_var_0;
4697 
4698     if (curFerrisWheelVar0 == 3)
4699     {
4700         ferris_wheel_var_0 = curFerrisWheelVar0;
4701         ferris_wheel_var_1 = curFerrisWheelVar0;
4702     }
4703     else if (curFerrisWheelVar0 < 3)
4704     {
4705         if (curFerrisWheelVar0 != -8)
4706             curFerrisWheelVar0--;
4707         ferris_wheel_var_0 = curFerrisWheelVar0;
4708         ferris_wheel_var_1 = -curFerrisWheelVar0;
4709     }
4710     else
4711     {
4712         curFerrisWheelVar0--;
4713         ferris_wheel_var_0 = curFerrisWheelVar0;
4714         ferris_wheel_var_1 = curFerrisWheelVar0;
4715     }
4716 
4717     uint8_t rotation = Pitch;
4718     if (curRide->mode == RideMode::ForwardRotation)
4719         rotation++;
4720     else
4721         rotation--;
4722 
4723     rotation &= 0x7F;
4724     Pitch = rotation;
4725 
4726     if (rotation == sub_state)
4727         var_CE++;
4728 
4729     Invalidate();
4730 
4731     uint8_t subState = sub_state;
4732     if (curRide->mode == RideMode::ForwardRotation)
4733         subState++;
4734     else
4735         subState--;
4736     subState &= 0x7F;
4737 
4738     if (subState == Pitch)
4739     {
4740         bool shouldStop = true;
4741         if (curRide->status != RideStatus::Closed)
4742         {
4743             if (var_CE < curRide->rotations)
4744                 shouldStop = false;
4745         }
4746 
4747         if (shouldStop)
4748         {
4749             curFerrisWheelVar0 = ferris_wheel_var_0;
4750             ferris_wheel_var_0 = -abs(curFerrisWheelVar0);
4751             ferris_wheel_var_1 = abs(curFerrisWheelVar0);
4752         }
4753     }
4754 
4755     if (ferris_wheel_var_0 != -8)
4756         return;
4757 
4758     subState = sub_state;
4759     if (curRide->mode == RideMode::ForwardRotation)
4760         subState += 8;
4761     else
4762         subState -= 8;
4763     subState &= 0x7F;
4764 
4765     if (subState != Pitch)
4766         return;
4767 
4768     SetState(Vehicle::Status::Arriving);
4769     var_C0 = 0;
4770 }
4771 
4772 /**
4773  *
4774  *  rct2: 0x006D94F2
4775  */
UpdateSimulatorOperating()4776 void Vehicle::UpdateSimulatorOperating()
4777 {
4778     if (_vehicleBreakdown == 0)
4779         return;
4780 
4781     assert(current_time >= -1);
4782     assert(current_time < MotionSimulatorTimeToSpriteMapCount);
4783     uint8_t al = MotionSimulatorTimeToSpriteMap[current_time + 1];
4784     if (al != 0xFF)
4785     {
4786         current_time++;
4787         if (al == Pitch)
4788             return;
4789         Pitch = al;
4790         Invalidate();
4791         return;
4792     }
4793 
4794     SetState(Vehicle::Status::Arriving);
4795     var_C0 = 0;
4796 }
4797 
4798 /**
4799  *
4800  *  rct2: 0x006D92FF
4801  */
UpdateRotating()4802 void Vehicle::UpdateRotating()
4803 {
4804     if (_vehicleBreakdown == 0)
4805         return;
4806 
4807     auto curRide = GetRide();
4808     if (curRide == nullptr)
4809         return;
4810 
4811     auto rideEntry = GetRideEntry();
4812     if (rideEntry == nullptr)
4813     {
4814         return;
4815     }
4816 
4817     const uint8_t* timeToSpriteMap;
4818     if (rideEntry->flags & RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_1)
4819     {
4820         timeToSpriteMap = Rotation1TimeToSpriteMaps[sub_state];
4821     }
4822     else if (rideEntry->flags & RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_2)
4823     {
4824         timeToSpriteMap = Rotation2TimeToSpriteMaps[sub_state];
4825     }
4826     else
4827     {
4828         timeToSpriteMap = Rotation3TimeToSpriteMaps[sub_state];
4829     }
4830 
4831     int32_t time = current_time;
4832     if (_vehicleBreakdown == BREAKDOWN_CONTROL_FAILURE)
4833     {
4834         time += (curRide->breakdown_sound_modifier >> 6) + 1;
4835     }
4836     time++;
4837 
4838     uint8_t sprite = timeToSpriteMap[static_cast<uint32_t>(time)];
4839     if (sprite != 0xFF)
4840     {
4841         current_time = static_cast<uint16_t>(time);
4842         if (sprite == Pitch)
4843             return;
4844         Pitch = sprite;
4845         Invalidate();
4846         return;
4847     }
4848 
4849     current_time = -1;
4850     var_CE++;
4851     if (_vehicleBreakdown != BREAKDOWN_CONTROL_FAILURE)
4852     {
4853         bool shouldStop = true;
4854         if (curRide->status != RideStatus::Closed)
4855         {
4856             sprite = var_CE + 1;
4857             if (curRide->type == RIDE_TYPE_ENTERPRISE)
4858                 sprite += 9;
4859 
4860             if (sprite < curRide->rotations)
4861                 shouldStop = false;
4862         }
4863 
4864         if (shouldStop)
4865         {
4866             if (sub_state == 2)
4867             {
4868                 SetState(Vehicle::Status::Arriving);
4869                 var_C0 = 0;
4870                 return;
4871             }
4872             sub_state++;
4873             UpdateRotating();
4874             return;
4875         }
4876     }
4877 
4878     if (curRide->type == RIDE_TYPE_ENTERPRISE && sub_state == 2)
4879     {
4880         SetState(Vehicle::Status::Arriving);
4881         var_C0 = 0;
4882         return;
4883     }
4884 
4885     sub_state = 1;
4886     UpdateRotating();
4887 }
4888 
4889 /**
4890  *
4891  *  rct2: 0x006D97CB
4892  */
UpdateSpaceRingsOperating()4893 void Vehicle::UpdateSpaceRingsOperating()
4894 {
4895     if (_vehicleBreakdown == 0)
4896         return;
4897 
4898     uint8_t spriteType = SpaceRingsTimeToSpriteMap[current_time + 1];
4899     if (spriteType != 255)
4900     {
4901         current_time++;
4902         if (spriteType != Pitch)
4903         {
4904             Pitch = spriteType;
4905             Invalidate();
4906         }
4907     }
4908     else
4909     {
4910         SetState(Vehicle::Status::Arriving);
4911         var_C0 = 0;
4912     }
4913 }
4914 
4915 /**
4916  *
4917  *  rct2: 0x006D9641
4918  */
UpdateHauntedHouseOperating()4919 void Vehicle::UpdateHauntedHouseOperating()
4920 {
4921     if (_vehicleBreakdown == 0)
4922         return;
4923 
4924     if (Pitch != 0)
4925     {
4926         if (gCurrentTicks & 1)
4927         {
4928             Pitch++;
4929             Invalidate();
4930 
4931             if (Pitch == 19)
4932                 Pitch = 0;
4933         }
4934     }
4935 
4936     if (current_time + 1 > 1500)
4937     {
4938         SetState(Vehicle::Status::Arriving);
4939         var_C0 = 0;
4940         return;
4941     }
4942 
4943     current_time++;
4944     switch (current_time)
4945     {
4946         case 45:
4947             OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::HauntedHouseScare, GetLocation());
4948             break;
4949         case 75:
4950             Pitch = 1;
4951             Invalidate();
4952             break;
4953         case 400:
4954             OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::HauntedHouseScream1, GetLocation());
4955             break;
4956         case 745:
4957             OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::HauntedHouseScare, GetLocation());
4958             break;
4959         case 775:
4960             Pitch = 1;
4961             Invalidate();
4962             break;
4963         case 1100:
4964             OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::HauntedHouseScream2, GetLocation());
4965             break;
4966     }
4967 }
4968 
4969 /**
4970  *
4971  *  rct2: 0x006d9781
4972  */
UpdateCrookedHouseOperating()4973 void Vehicle::UpdateCrookedHouseOperating()
4974 {
4975     if (_vehicleBreakdown == 0)
4976         return;
4977 
4978     // Originally used an array of size 1 at 0x009A0AC4 and passed the sub state into it.
4979     if (static_cast<uint16_t>(current_time + 1) > 600)
4980     {
4981         SetState(Vehicle::Status::Arriving);
4982         var_C0 = 0;
4983         return;
4984     }
4985 
4986     current_time++;
4987 }
4988 
4989 /**
4990  *
4991  *  rct2: 0x006D9547
4992  */
UpdateTopSpinOperating()4993 void Vehicle::UpdateTopSpinOperating()
4994 {
4995     if (_vehicleBreakdown == 0)
4996         return;
4997 
4998     const top_spin_time_to_sprite_map* sprite_map = TopSpinTimeToSpriteMaps[sub_state];
4999     uint8_t rotation = sprite_map[current_time + 1].arm_rotation;
5000     if (rotation != 0xFF)
5001     {
5002         current_time = current_time + 1;
5003         if (rotation != Pitch)
5004         {
5005             Pitch = rotation;
5006             Invalidate();
5007         }
5008         rotation = sprite_map[current_time].bank_rotation;
5009         if (rotation != bank_rotation)
5010         {
5011             bank_rotation = rotation;
5012             Invalidate();
5013         }
5014         return;
5015     }
5016 
5017     SetState(Vehicle::Status::Arriving);
5018     var_C0 = 0;
5019 }
5020 
5021 /**
5022  *
5023  *  rct2: 0x006D95AD
5024  */
UpdateShowingFilm()5025 void Vehicle::UpdateShowingFilm()
5026 {
5027     int32_t currentTime, totalTime;
5028 
5029     if (_vehicleBreakdown == 0)
5030         return;
5031 
5032     totalTime = RideFilmLength[sub_state];
5033     currentTime = current_time + 1;
5034     if (currentTime <= totalTime)
5035     {
5036         current_time = currentTime;
5037     }
5038     else
5039     {
5040         SetState(Vehicle::Status::Arriving);
5041         var_C0 = 0;
5042     }
5043 }
5044 
5045 /**
5046  *
5047  *  rct2: 0x006D95F7
5048  */
UpdateDoingCircusShow()5049 void Vehicle::UpdateDoingCircusShow()
5050 {
5051     if (_vehicleBreakdown == 0)
5052         return;
5053 
5054     int32_t currentTime = current_time + 1;
5055     if (currentTime <= 5000)
5056     {
5057         current_time = currentTime;
5058     }
5059     else
5060     {
5061         SetState(Vehicle::Status::Arriving);
5062         var_C0 = 0;
5063     }
5064 }
5065 
5066 /**
5067  *
5068  *  rct2: 0x0068B8BD
5069  * @returns the map element that the vehicle will collide with or NULL if no collisions.
5070  */
vehicle_check_collision(const CoordsXYZ & vehiclePosition)5071 static TileElement* vehicle_check_collision(const CoordsXYZ& vehiclePosition)
5072 {
5073     TileElement* tileElement = map_get_first_element_at(vehiclePosition);
5074     if (tileElement == nullptr)
5075     {
5076         return nullptr;
5077     }
5078 
5079     uint8_t quadrant;
5080     if ((vehiclePosition.x & 0x1F) >= 16)
5081     {
5082         quadrant = 1;
5083         if ((vehiclePosition.y & 0x1F) < 16)
5084             quadrant = 2;
5085     }
5086     else
5087     {
5088         quadrant = 4;
5089         if ((vehiclePosition.y & 0x1F) >= 16)
5090             quadrant = 8;
5091     }
5092 
5093     do
5094     {
5095         if (vehiclePosition.z < tileElement->GetBaseZ())
5096             continue;
5097 
5098         if (vehiclePosition.z >= tileElement->GetClearanceZ())
5099             continue;
5100 
5101         if (tileElement->GetOccupiedQuadrants() & quadrant)
5102             return tileElement;
5103     } while (!(tileElement++)->IsLastForTile());
5104 
5105     return nullptr;
5106 }
5107 
ride_train_crash(Ride * ride,uint16_t numFatalities)5108 static void ride_train_crash(Ride* ride, uint16_t numFatalities)
5109 {
5110     Formatter ft;
5111     ft.Add<uint16_t>(numFatalities);
5112 
5113     uint8_t crashType = numFatalities == 0 ? RIDE_CRASH_TYPE_NO_FATALITIES : RIDE_CRASH_TYPE_FATALITIES;
5114 
5115     if (crashType >= ride->last_crash_type)
5116         ride->last_crash_type = crashType;
5117 
5118     if (numFatalities != 0)
5119     {
5120         if (gConfigNotifications.ride_casualties)
5121         {
5122             ride->FormatNameTo(ft);
5123             News::AddItemToQueue(
5124                 News::ItemType::Ride, numFatalities == 1 ? STR_X_PERSON_DIED_ON_X : STR_X_PEOPLE_DIED_ON_X, EnumValue(ride->id),
5125                 ft);
5126         }
5127 
5128         if (gParkRatingCasualtyPenalty < 500)
5129         {
5130             gParkRatingCasualtyPenalty += 200;
5131         }
5132     }
5133 }
5134 /**
5135  *
5136  *  rct2: 0x006DE6C6
5137  */
KillAllPassengersInTrain()5138 void Vehicle::KillAllPassengersInTrain()
5139 {
5140     auto curRide = GetRide();
5141     if (curRide == nullptr)
5142         return;
5143 
5144     ride_train_crash(curRide, NumPeepsUntilTrainTail());
5145 
5146     for (Vehicle* trainCar = GetEntity<Vehicle>(sprite_index); trainCar != nullptr;
5147          trainCar = GetEntity<Vehicle>(trainCar->next_vehicle_on_train))
5148     {
5149         trainCar->KillPassengers(curRide);
5150     }
5151 }
5152 
KillPassengers(Ride * curRide)5153 void Vehicle::KillPassengers(Ride* curRide)
5154 {
5155     if (num_peeps != next_free_seat)
5156         return;
5157 
5158     if (num_peeps == 0)
5159         return;
5160 
5161     for (auto i = 0; i < num_peeps; i++)
5162     {
5163         auto* curPeep = GetEntity<Guest>(peep[i]);
5164         if (curPeep == nullptr)
5165             continue;
5166 
5167         if (!curPeep->OutsideOfPark)
5168         {
5169             decrement_guests_in_park();
5170             auto intent = Intent(INTENT_ACTION_UPDATE_GUEST_COUNT);
5171             context_broadcast_intent(&intent);
5172         }
5173         peep_sprite_remove(curPeep);
5174     }
5175 
5176     num_peeps = 0;
5177     next_free_seat = 0;
5178 }
5179 
CrashOnLand()5180 void Vehicle::CrashOnLand()
5181 {
5182     auto curRide = GetRide();
5183     if (curRide == nullptr)
5184         return;
5185 
5186     if (curRide->status == RideStatus::Simulating)
5187     {
5188         SimulateCrash();
5189         return;
5190     }
5191     SetState(Vehicle::Status::Crashed, sub_state);
5192 
5193 #ifdef ENABLE_SCRIPTING
5194     InvokeVehicleCrashHook(sprite_index, "land");
5195 #endif
5196 
5197     if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_CRASHED))
5198     {
5199         auto frontVehicle = GetHead();
5200         auto trainIndex = ride_get_train_index_from_vehicle(curRide, frontVehicle->sprite_index);
5201         if (!trainIndex.has_value())
5202         {
5203             return;
5204         }
5205 
5206         curRide->Crash(trainIndex.value());
5207 
5208         if (curRide->status != RideStatus::Closed)
5209         {
5210             // We require this to execute right away during the simulation, always ignore network and queue.
5211             auto gameAction = RideSetStatusAction(curRide->id, RideStatus::Closed);
5212             GameActions::ExecuteNested(&gameAction);
5213         }
5214     }
5215     curRide->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED;
5216     curRide->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
5217 
5218     if (IsHead())
5219     {
5220         KillAllPassengersInTrain();
5221     }
5222 
5223     sub_state = 2;
5224 
5225     const auto curLoc = GetLocation();
5226     OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Crash, curLoc);
5227 
5228     ExplosionCloud::Create(curLoc);
5229     ExplosionFlare::Create(curLoc);
5230 
5231     uint8_t numParticles = std::min(sprite_width, static_cast<uint8_t>(7));
5232 
5233     while (numParticles-- != 0)
5234         VehicleCrashParticle::Create(colours, curLoc);
5235 
5236     IsCrashedVehicle = true;
5237     animation_frame = 0;
5238     animationState = 0;
5239     sprite_width = 13;
5240     sprite_height_negative = 45;
5241     sprite_height_positive = 5;
5242 
5243     MoveTo(curLoc);
5244 
5245     crash_z = 0;
5246 }
5247 
CrashOnWater()5248 void Vehicle::CrashOnWater()
5249 {
5250     auto curRide = GetRide();
5251     if (curRide == nullptr)
5252         return;
5253 
5254     if (curRide->status == RideStatus::Simulating)
5255     {
5256         SimulateCrash();
5257         return;
5258     }
5259     SetState(Vehicle::Status::Crashed, sub_state);
5260 
5261 #ifdef ENABLE_SCRIPTING
5262     InvokeVehicleCrashHook(sprite_index, "water");
5263 #endif
5264 
5265     if (!(curRide->lifecycle_flags & RIDE_LIFECYCLE_CRASHED))
5266     {
5267         auto frontVehicle = GetHead();
5268         auto trainIndex = ride_get_train_index_from_vehicle(curRide, frontVehicle->sprite_index);
5269         if (!trainIndex.has_value())
5270         {
5271             return;
5272         }
5273 
5274         curRide->Crash(trainIndex.value());
5275 
5276         if (curRide->status != RideStatus::Closed)
5277         {
5278             // We require this to execute right away during the simulation, always ignore network and queue.
5279             auto gameAction = RideSetStatusAction(curRide->id, RideStatus::Closed);
5280             GameActions::ExecuteNested(&gameAction);
5281         }
5282     }
5283     curRide->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED;
5284     curRide->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
5285 
5286     if (IsHead())
5287     {
5288         KillAllPassengersInTrain();
5289     }
5290 
5291     sub_state = 2;
5292 
5293     const auto curLoc = GetLocation();
5294     OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Water1, curLoc);
5295 
5296     CrashSplashParticle::Create(curLoc);
5297     CrashSplashParticle::Create(curLoc + CoordsXYZ{ -8, -9, 0 });
5298     CrashSplashParticle::Create(curLoc + CoordsXYZ{ 11, -9, 0 });
5299     CrashSplashParticle::Create(curLoc + CoordsXYZ{ 11, 8, 0 });
5300     CrashSplashParticle::Create(curLoc + CoordsXYZ{ -4, 8, 0 });
5301 
5302     for (int32_t i = 0; i < 10; ++i)
5303         VehicleCrashParticle::Create(colours, curLoc + CoordsXYZ{ -4, 8, 0 });
5304 
5305     IsCrashedVehicle = true;
5306     animation_frame = 0;
5307     animationState = 0;
5308     sprite_width = 13;
5309     sprite_height_negative = 45;
5310     sprite_height_positive = 5;
5311 
5312     MoveTo(curLoc);
5313 
5314     crash_z = -1;
5315 }
5316 
5317 /**
5318  *
5319  *  rct2: 0x006D98CA
5320  */
UpdateCrash()5321 void Vehicle::UpdateCrash()
5322 {
5323     for (Vehicle* curVehicle = GetEntity<Vehicle>(sprite_index); curVehicle != nullptr;
5324          curVehicle = GetEntity<Vehicle>(curVehicle->next_vehicle_on_train))
5325     {
5326         CoordsXYZ curPos = curVehicle->GetLocation();
5327 
5328         if (curVehicle->sub_state > 1)
5329         {
5330             if (curVehicle->crash_z <= 96)
5331             {
5332                 curVehicle->crash_z++;
5333                 if ((scenario_rand() & 0xFFFF) <= 0x1555)
5334                 {
5335                     int32_t xOffset = (scenario_rand() & 2) - 1;
5336                     int32_t yOffset = (scenario_rand() & 2) - 1;
5337 
5338                     ExplosionCloud::Create(curPos + CoordsXYZ{ xOffset, yOffset, 0 });
5339                 }
5340             }
5341             if (curVehicle->animationState <= 0xe388)
5342             {
5343                 curVehicle->animationState += 0x1c71;
5344             }
5345             else
5346             {
5347                 curVehicle->animationState = 0;
5348                 curVehicle->animation_frame++;
5349                 if (curVehicle->animation_frame >= 8)
5350                     curVehicle->animation_frame = 0;
5351                 curVehicle->Invalidate();
5352             }
5353             continue;
5354         }
5355 
5356         TileElement* collideElement = vehicle_check_collision(curPos);
5357         if (collideElement == nullptr)
5358         {
5359             curVehicle->sub_state = 1;
5360         }
5361         else if (curVehicle->sub_state == 1)
5362         {
5363             curVehicle->CrashOnLand();
5364             continue;
5365         }
5366 
5367         int16_t height = tile_element_height(curPos);
5368         int16_t waterHeight = tile_element_water_height(curPos);
5369         int16_t zDiff;
5370         if (waterHeight != 0)
5371         {
5372             zDiff = curPos.z - waterHeight;
5373             if (zDiff <= 0 && zDiff >= -20)
5374             {
5375                 curVehicle->CrashOnWater();
5376                 continue;
5377             }
5378         }
5379 
5380         zDiff = curPos.z - height;
5381         if ((zDiff <= 0 && zDiff >= -20) || curPos.z < 16)
5382         {
5383             curVehicle->CrashOnLand();
5384             continue;
5385         }
5386 
5387         curVehicle->Invalidate();
5388 
5389         curPos.x += static_cast<int8_t>(curVehicle->crash_x >> 8);
5390         curPos.y += static_cast<int8_t>(curVehicle->crash_y >> 8);
5391         curPos.z += static_cast<int8_t>(curVehicle->crash_z >> 8);
5392         curVehicle->TrackLocation = { (curVehicle->crash_x << 8), (curVehicle->crash_y << 8), (curVehicle->crash_z << 8) };
5393 
5394         if (!map_is_location_valid(curPos))
5395         {
5396             curVehicle->CrashOnLand();
5397             continue;
5398         }
5399 
5400         curVehicle->MoveTo(curPos);
5401 
5402         if (curVehicle->sub_state == 1)
5403         {
5404             curVehicle->crash_z -= 20;
5405         }
5406     }
5407 }
5408 /**
5409  *
5410  *  rct2: 0x006D7888
5411  */
UpdateSound()5412 void Vehicle::UpdateSound()
5413 {
5414     // frictionVolume (bl) should be set before hand
5415     SoundIdVolume frictionSound = { OpenRCT2::Audio::SoundId::Null, 255 };
5416     // bh screamVolume should be set before hand
5417     SoundIdVolume screamSound = { OpenRCT2::Audio::SoundId::Null, 255 };
5418 
5419     auto curRide = GetRide();
5420     if (curRide == nullptr)
5421         return;
5422 
5423     auto rideEntry = GetRideEntry();
5424     if (rideEntry == nullptr)
5425         return;
5426 
5427     rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle_type];
5428 
5429     int32_t ecx = abs(velocity) - 0x10000;
5430     if (ecx >= 0)
5431     {
5432         frictionSound.id = vehicleEntry->friction_sound_id;
5433         ecx >>= 15;
5434         frictionSound.volume = std::min(208 + (ecx & 0xFF), 255);
5435     }
5436 
5437     switch (vehicleEntry->sound_range)
5438     {
5439         case SOUND_RANGE_WHISTLE:
5440             screamSound.id = scream_sound_id;
5441             if (!(gCurrentTicks & 0x7F))
5442             {
5443                 if (velocity < 0x40000 || scream_sound_id != OpenRCT2::Audio::SoundId::Null)
5444                 {
5445                     GetLiftHillSound(curRide, screamSound);
5446                     break;
5447                 }
5448 
5449                 if ((scenario_rand() & 0xFFFF) <= 0x5555)
5450                 {
5451                     scream_sound_id = OpenRCT2::Audio::SoundId::TrainWhistle;
5452                     screamSound.volume = 255;
5453                     break;
5454                 }
5455             }
5456             if (screamSound.id == OpenRCT2::Audio::SoundId::NoScream)
5457                 screamSound.id = OpenRCT2::Audio::SoundId::Null;
5458             screamSound.volume = 255;
5459             break;
5460 
5461         case SOUND_RANGE_BELL:
5462             screamSound.id = scream_sound_id;
5463             if (!(gCurrentTicks & 0x7F))
5464             {
5465                 if (velocity < 0x40000 || scream_sound_id != OpenRCT2::Audio::SoundId::Null)
5466                 {
5467                     GetLiftHillSound(curRide, screamSound);
5468                     break;
5469                 }
5470 
5471                 if ((scenario_rand() & 0xFFFF) <= 0x5555)
5472                 {
5473                     scream_sound_id = OpenRCT2::Audio::SoundId::Tram;
5474                     screamSound.volume = 255;
5475                     break;
5476                 }
5477             }
5478             if (screamSound.id == OpenRCT2::Audio::SoundId::NoScream)
5479                 screamSound.id = OpenRCT2::Audio::SoundId::Null;
5480             screamSound.volume = 255;
5481             break;
5482 
5483         default:
5484             if ((vehicleEntry->flags & VEHICLE_ENTRY_FLAG_RIDERS_SCREAM))
5485             {
5486                 screamSound.id = UpdateScreamSound();
5487                 if (screamSound.id == OpenRCT2::Audio::SoundId::NoScream)
5488                 {
5489                     screamSound.id = OpenRCT2::Audio::SoundId::Null;
5490                     break;
5491                 }
5492                 if (screamSound.id != OpenRCT2::Audio::SoundId::Null)
5493                 {
5494                     break;
5495                 }
5496             }
5497             GetLiftHillSound(curRide, screamSound);
5498     }
5499 
5500     // Friction sound
5501     auto soundIdVolume = sub_6D7AC0(sound1_id, sound1_volume, frictionSound.id, frictionSound.volume);
5502     sound1_id = soundIdVolume.id;
5503     sound1_volume = soundIdVolume.volume;
5504 
5505     // Scream sound
5506     soundIdVolume = sub_6D7AC0(sound2_id, sound2_volume, screamSound.id, screamSound.volume);
5507     sound2_id = soundIdVolume.id;
5508     sound2_volume = soundIdVolume.volume;
5509 
5510     // Calculate Sound Vector (used for sound frequency calcs)
5511     int32_t soundDirection = SpriteDirectionToSoundDirection[sprite_direction];
5512     int32_t soundVector = ((velocity >> 14) * soundDirection) >> 14;
5513     soundVector = std::clamp(soundVector, -127, 127);
5514 
5515     sound_vector_factor = soundVector & 0xFF;
5516 }
5517 
5518 /**
5519  *
5520  *  rct2: 0x006D796B
5521  */
UpdateScreamSound()5522 OpenRCT2::Audio::SoundId Vehicle::UpdateScreamSound()
5523 {
5524     int32_t totalNumPeeps = NumPeepsUntilTrainTail();
5525     if (totalNumPeeps == 0)
5526         return OpenRCT2::Audio::SoundId::Null;
5527 
5528     if (velocity < 0)
5529     {
5530         if (velocity > -0x2C000)
5531             return OpenRCT2::Audio::SoundId::Null;
5532 
5533         for (Vehicle* vehicle2 = GetEntity<Vehicle>(sprite_index); vehicle2 != nullptr;
5534              vehicle2 = GetEntity<Vehicle>(vehicle2->next_vehicle_on_train))
5535         {
5536             if (vehicle2->Pitch < 1)
5537                 continue;
5538             if (vehicle2->Pitch <= 4)
5539                 return ProduceScreamSound(totalNumPeeps);
5540             if (vehicle2->Pitch < 9)
5541                 continue;
5542             if (vehicle2->Pitch <= 15)
5543                 return ProduceScreamSound(totalNumPeeps);
5544         }
5545         return OpenRCT2::Audio::SoundId::Null;
5546     }
5547 
5548     if (velocity < 0x2C000)
5549         return OpenRCT2::Audio::SoundId::Null;
5550 
5551     for (Vehicle* vehicle2 = GetEntity<Vehicle>(sprite_index); vehicle2 != nullptr;
5552          vehicle2 = GetEntity<Vehicle>(vehicle2->next_vehicle_on_train))
5553     {
5554         if (vehicle2->Pitch < 5)
5555             continue;
5556         if (vehicle2->Pitch <= 8)
5557             return ProduceScreamSound(totalNumPeeps);
5558         if (vehicle2->Pitch < 17)
5559             continue;
5560         if (vehicle2->Pitch <= 23)
5561             return ProduceScreamSound(totalNumPeeps);
5562     }
5563     return OpenRCT2::Audio::SoundId::Null;
5564 }
5565 
ProduceScreamSound(const int32_t totalNumPeeps)5566 OpenRCT2::Audio::SoundId Vehicle::ProduceScreamSound(const int32_t totalNumPeeps)
5567 {
5568     rct_ride_entry* rideEntry = GetRideEntry();
5569 
5570     rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle_type];
5571 
5572     if (scream_sound_id == OpenRCT2::Audio::SoundId::Null)
5573     {
5574         auto r = scenario_rand();
5575         if (totalNumPeeps >= static_cast<int32_t>(r % 16))
5576         {
5577             switch (vehicleEntry->sound_range)
5578             {
5579                 case SOUND_RANGE_SCREAMS_0:
5580                     scream_sound_id = byte_9A3A14[r % 2];
5581                     break;
5582                 case SOUND_RANGE_SCREAMS_1:
5583                     scream_sound_id = byte_9A3A18[r % 7];
5584                     break;
5585                 case SOUND_RANGE_SCREAMS_2:
5586                     scream_sound_id = byte_9A3A16[r % 2];
5587                     break;
5588                 default:
5589                     scream_sound_id = OpenRCT2::Audio::SoundId::NoScream;
5590                     break;
5591             }
5592         }
5593         else
5594         {
5595             scream_sound_id = OpenRCT2::Audio::SoundId::NoScream;
5596         }
5597     }
5598     return scream_sound_id;
5599 }
5600 
5601 /**
5602  *
5603  *  rct2: 0x006D73D0
5604  * ax: verticalG
5605  * dx: lateralG
5606  * esi: vehicle
5607  */
GetGForces() const5608 GForces Vehicle::GetGForces() const
5609 {
5610     int32_t gForceVert = ((static_cast<int64_t>(0x280000)) * Unk9A37E4[Pitch]) >> 32;
5611     gForceVert = ((static_cast<int64_t>(gForceVert)) * Unk9A39C4[bank_rotation]) >> 32;
5612     int32_t lateralFactor = 0, vertFactor = 0;
5613 
5614     // Note shr has meant some of the below functions cast a known negative number to
5615     // unsigned. Possibly an original bug but will be left implemented.
5616     switch (GetTrackType())
5617     {
5618         case TrackElemType::Flat:
5619         case TrackElemType::EndStation:
5620         case TrackElemType::BeginStation:
5621         case TrackElemType::MiddleStation:
5622         case TrackElemType::Up25:
5623         case TrackElemType::Up60: //
5624         case TrackElemType::Down25:
5625         case TrackElemType::Down60: //
5626         case TrackElemType::FlatToLeftBank:
5627         case TrackElemType::FlatToRightBank:
5628         case TrackElemType::LeftBankToFlat:
5629         case TrackElemType::RightBankToFlat: //
5630         case TrackElemType::LeftBank:
5631         case TrackElemType::RightBank:
5632         case TrackElemType::TowerBase:
5633         case TrackElemType::TowerSection:
5634         case TrackElemType::FlatCovered:
5635         case TrackElemType::Up25Covered:
5636         case TrackElemType::Up60Covered:
5637         case TrackElemType::Down25Covered:
5638         case TrackElemType::Down60Covered:
5639         case TrackElemType::Brakes:
5640         case TrackElemType::RotationControlToggle:
5641         case TrackElemType::Maze:
5642         case TrackElemType::Up25LeftBanked:
5643         case TrackElemType::Up25RightBanked:
5644         case TrackElemType::Waterfall:
5645         case TrackElemType::Rapids:
5646         case TrackElemType::OnRidePhoto:
5647         case TrackElemType::Down25LeftBanked:
5648         case TrackElemType::Down25RightBanked:
5649         case TrackElemType::Whirlpool:
5650         case TrackElemType::ReverseFreefallVertical:
5651         case TrackElemType::Up90:
5652         case TrackElemType::Down90:
5653         case TrackElemType::DiagFlat:
5654         case TrackElemType::DiagUp25:
5655         case TrackElemType::DiagUp60:
5656         case TrackElemType::DiagDown25:
5657         case TrackElemType::DiagDown60:
5658         case TrackElemType::DiagFlatToLeftBank:
5659         case TrackElemType::DiagFlatToRightBank:
5660         case TrackElemType::DiagLeftBankToFlat:
5661         case TrackElemType::DiagRightBankToFlat:
5662         case TrackElemType::DiagLeftBank:
5663         case TrackElemType::DiagRightBank:
5664         case TrackElemType::LogFlumeReverser:
5665         case TrackElemType::SpinningTunnel:
5666         case TrackElemType::PoweredLift:
5667         case TrackElemType::MinigolfHoleA:
5668         case TrackElemType::MinigolfHoleB:
5669         case TrackElemType::MinigolfHoleC:
5670         case TrackElemType::MinigolfHoleD:
5671         case TrackElemType::MinigolfHoleE:
5672         case TrackElemType::LeftReverser:
5673         case TrackElemType::RightReverser:
5674         case TrackElemType::AirThrustVerticalDown:
5675         case TrackElemType::BlockBrakes:
5676         case TrackElemType::Up25ToLeftBankedUp25:
5677         case TrackElemType::Up25ToRightBankedUp25:
5678         case TrackElemType::LeftBankedUp25ToUp25:
5679         case TrackElemType::RightBankedUp25ToUp25:
5680         case TrackElemType::Down25ToLeftBankedDown25:
5681         case TrackElemType::Down25ToRightBankedDown25:
5682         case TrackElemType::LeftBankedDown25ToDown25:
5683         case TrackElemType::RightBankedDown25ToDown25:
5684         case TrackElemType::LeftQuarterTurn1TileUp90:
5685         case TrackElemType::RightQuarterTurn1TileUp90:
5686         case TrackElemType::LeftQuarterTurn1TileDown90:
5687         case TrackElemType::RightQuarterTurn1TileDown90:
5688             // 6d73FF
5689             // Do nothing
5690             break;
5691         case TrackElemType::FlatToUp25:   //
5692         case TrackElemType::Down25ToFlat: //
5693         case TrackElemType::LeftBankToUp25:
5694         case TrackElemType::RightBankToUp25:
5695         case TrackElemType::Down25ToLeftBank:
5696         case TrackElemType::Down25ToRightBank:
5697         case TrackElemType::FlatToUp25Covered:
5698         case TrackElemType::Down25ToFlatCovered:
5699         case TrackElemType::LeftBankedFlatToLeftBankedUp25:
5700         case TrackElemType::RightBankedFlatToRightBankedUp25:
5701         case TrackElemType::LeftBankedDown25ToLeftBankedFlat:
5702         case TrackElemType::RightBankedDown25ToRightBankedFlat:
5703         case TrackElemType::FlatToLeftBankedUp25:
5704         case TrackElemType::FlatToRightBankedUp25:
5705         case TrackElemType::LeftBankedDown25ToFlat:
5706         case TrackElemType::RightBankedDown25ToFlat:
5707             vertFactor = 103;
5708             // 6d7509
5709             break;
5710         case TrackElemType::Up25ToFlat:   //
5711         case TrackElemType::FlatToDown25: //
5712         case TrackElemType::Up25ToLeftBank:
5713         case TrackElemType::Up25ToRightBank:
5714         case TrackElemType::LeftBankToDown25:
5715         case TrackElemType::RightBankToDown25:
5716         case TrackElemType::Up25ToFlatCovered:
5717         case TrackElemType::FlatToDown25Covered:
5718         case TrackElemType::CableLiftHill:
5719         case TrackElemType::LeftBankedUp25ToLeftBankedFlat:
5720         case TrackElemType::RightBankedUp25ToRightBankedFlat:
5721         case TrackElemType::LeftBankedFlatToLeftBankedDown25:
5722         case TrackElemType::RightBankedFlatToRightBankedDown25:
5723         case TrackElemType::LeftBankedUp25ToFlat:
5724         case TrackElemType::RightBankedUp25ToFlat:
5725         case TrackElemType::FlatToLeftBankedDown25:
5726         case TrackElemType::FlatToRightBankedDown25:
5727             vertFactor = -103;
5728             // 6d7569
5729             break;
5730         case TrackElemType::Up25ToUp60:     //
5731         case TrackElemType::Down60ToDown25: //
5732         case TrackElemType::Up25ToUp60Covered:
5733         case TrackElemType::Down60ToDown25Covered:
5734             vertFactor = 82;
5735             // 6d7545
5736             break;
5737         case TrackElemType::Up60ToUp25:     //
5738         case TrackElemType::Down25ToDown60: //
5739         case TrackElemType::Up60ToUp25Covered:
5740         case TrackElemType::Down25ToDown60Covered:
5741             vertFactor = -82;
5742             // 6d7551
5743             break;
5744         case TrackElemType::LeftQuarterTurn5Tiles: //
5745         case TrackElemType::LeftQuarterTurn5TilesUp25:
5746         case TrackElemType::LeftQuarterTurn5TilesDown25:
5747         case TrackElemType::LeftTwistDownToUp:
5748         case TrackElemType::LeftTwistUpToDown:
5749         case TrackElemType::LeftQuarterTurn5TilesCovered:
5750         case TrackElemType::LeftQuarterHelixLargeUp:
5751         case TrackElemType::LeftQuarterHelixLargeDown:
5752         case TrackElemType::LeftFlyerTwistUp:
5753         case TrackElemType::LeftFlyerTwistDown:
5754         case TrackElemType::LeftHeartLineRoll:
5755             lateralFactor = 98;
5756             // 6d7590
5757             break;
5758         case TrackElemType::RightQuarterTurn5Tiles: //
5759         case TrackElemType::RightQuarterTurn5TilesUp25:
5760         case TrackElemType::RightQuarterTurn5TilesDown25:
5761         case TrackElemType::RightTwistDownToUp:
5762         case TrackElemType::RightTwistUpToDown:
5763         case TrackElemType::RightQuarterTurn5TilesCovered:
5764         case TrackElemType::RightQuarterHelixLargeUp:
5765         case TrackElemType::RightQuarterHelixLargeDown:
5766         case TrackElemType::RightFlyerTwistUp:
5767         case TrackElemType::RightFlyerTwistDown:
5768         case TrackElemType::RightHeartLineRoll:
5769             lateralFactor = -98;
5770             // 6d75B7
5771             break;
5772         case TrackElemType::BankedLeftQuarterTurn5Tiles:
5773         case TrackElemType::LeftHalfBankedHelixUpLarge:
5774         case TrackElemType::LeftHalfBankedHelixDownLarge:
5775         case TrackElemType::LeftQuarterBankedHelixLargeUp:
5776         case TrackElemType::LeftQuarterBankedHelixLargeDown:
5777             vertFactor = 200;
5778             lateralFactor = 160;
5779             // 6d75E1
5780             break;
5781         case TrackElemType::BankedRightQuarterTurn5Tiles:
5782         case TrackElemType::RightHalfBankedHelixUpLarge:
5783         case TrackElemType::RightHalfBankedHelixDownLarge:
5784         case TrackElemType::RightQuarterBankedHelixLargeUp:
5785         case TrackElemType::RightQuarterBankedHelixLargeDown:
5786             vertFactor = 200;
5787             lateralFactor = -160;
5788             // 6d75F0
5789             break;
5790         case TrackElemType::SBendLeft:
5791         case TrackElemType::SBendLeftCovered:
5792             lateralFactor = (track_progress < 48) ? 98 : -98;
5793             // 6d75FF
5794             break;
5795         case TrackElemType::SBendRight:
5796         case TrackElemType::SBendRightCovered:
5797             lateralFactor = (track_progress < 48) ? -98 : 98;
5798             // 6d7608
5799             break;
5800         case TrackElemType::LeftVerticalLoop:
5801         case TrackElemType::RightVerticalLoop:
5802             vertFactor = (abs(track_progress - 155) / 2) + 28;
5803             // 6d7690
5804             break;
5805         case TrackElemType::LeftQuarterTurn3Tiles:
5806         case TrackElemType::LeftQuarterTurn3TilesUp25:
5807         case TrackElemType::LeftQuarterTurn3TilesDown25:
5808         case TrackElemType::LeftQuarterTurn3TilesCovered:
5809         case TrackElemType::LeftCurvedLiftHill:
5810             lateralFactor = 59;
5811             // 6d7704
5812             break;
5813         case TrackElemType::RightQuarterTurn3Tiles:
5814         case TrackElemType::RightQuarterTurn3TilesUp25:
5815         case TrackElemType::RightQuarterTurn3TilesDown25:
5816         case TrackElemType::RightQuarterTurn3TilesCovered:
5817         case TrackElemType::RightCurvedLiftHill:
5818             lateralFactor = -59;
5819             // 6d7710
5820             break;
5821         case TrackElemType::LeftBankedQuarterTurn3Tiles:
5822         case TrackElemType::LeftHalfBankedHelixUpSmall:
5823         case TrackElemType::LeftHalfBankedHelixDownSmall:
5824             vertFactor = 100;
5825             lateralFactor = 100;
5826             // 6d7782
5827             break;
5828         case TrackElemType::RightBankedQuarterTurn3Tiles:
5829         case TrackElemType::RightHalfBankedHelixUpSmall:
5830         case TrackElemType::RightHalfBankedHelixDownSmall:
5831             vertFactor = 100;
5832             lateralFactor = -100;
5833             // 6d778E
5834             break;
5835         case TrackElemType::LeftQuarterTurn1Tile:
5836             lateralFactor = 45;
5837             // 6d779A
5838             break;
5839         case TrackElemType::RightQuarterTurn1Tile:
5840             lateralFactor = -45;
5841             // 6d77A3
5842             break;
5843         case TrackElemType::HalfLoopUp:
5844         case TrackElemType::FlyerHalfLoopUp:
5845             vertFactor = ((static_cast<uint16_t>(-(track_progress - 155))) / 2) + 28;
5846             // 6d763E
5847             break;
5848         case TrackElemType::HalfLoopDown:
5849         case TrackElemType::FlyerHalfLoopDown:
5850             vertFactor = (track_progress / 2) + 28;
5851             // 6d7656
5852             break;
5853         case TrackElemType::LeftCorkscrewUp:
5854         case TrackElemType::RightCorkscrewDown:
5855         case TrackElemType::LeftFlyerCorkscrewUp:
5856         case TrackElemType::RightFlyerCorkscrewDown:
5857             vertFactor = 52;
5858             lateralFactor = 70;
5859             // 6d76AA
5860             break;
5861         case TrackElemType::RightCorkscrewUp:
5862         case TrackElemType::LeftCorkscrewDown:
5863         case TrackElemType::RightFlyerCorkscrewUp:
5864         case TrackElemType::LeftFlyerCorkscrewDown:
5865             vertFactor = 52;
5866             lateralFactor = -70;
5867             // 6d76B9
5868             break;
5869         case TrackElemType::FlatToUp60:
5870         case TrackElemType::Down60ToFlat:
5871             vertFactor = 56;
5872             // 6d747C
5873             break;
5874         case TrackElemType::Up60ToFlat:
5875         case TrackElemType::FlatToDown60:
5876         case TrackElemType::BrakeForDrop:
5877             vertFactor = -56;
5878             // 6d7488
5879             break;
5880         case TrackElemType::LeftQuarterTurn1TileUp60:
5881         case TrackElemType::LeftQuarterTurn1TileDown60:
5882             lateralFactor = 88;
5883             // 6d7770
5884             break;
5885         case TrackElemType::RightQuarterTurn1TileUp60:
5886         case TrackElemType::RightQuarterTurn1TileDown60:
5887             lateralFactor = -88;
5888             // 6d7779
5889             break;
5890         case TrackElemType::Watersplash:
5891             vertFactor = -150;
5892             if (track_progress < 32)
5893                 break;
5894             vertFactor = 150;
5895             if (track_progress < 64)
5896                 break;
5897             vertFactor = 0;
5898             if (track_progress < 96)
5899                 break;
5900             vertFactor = 150;
5901             if (track_progress < 128)
5902                 break;
5903             vertFactor = -150;
5904             // 6d7408
5905             break;
5906         case TrackElemType::FlatToUp60LongBase:
5907         case TrackElemType::Down60ToFlatLongBase:
5908             vertFactor = 160;
5909             // 6d74F1
5910             break;
5911         case TrackElemType::Up60ToFlatLongBase:
5912         case TrackElemType::FlatToDown60LongBase:
5913             vertFactor = -160;
5914             // 6d74FD
5915             break;
5916         case TrackElemType::ReverseFreefallSlope:
5917         case TrackElemType::AirThrustVerticalDownToLevel:
5918             vertFactor = 120;
5919             // 6d7458
5920             break;
5921         case TrackElemType::Up60ToUp90:
5922         case TrackElemType::Down90ToDown60:
5923             vertFactor = 110;
5924             // 6d7515
5925             break;
5926         case TrackElemType::Up90ToUp60:
5927         case TrackElemType::Down60ToDown90:
5928             vertFactor = -110;
5929             // 6d7521
5930             break;
5931         case TrackElemType::LeftEighthToDiag:
5932         case TrackElemType::LeftEighthToOrthogonal:
5933             lateralFactor = 137;
5934             // 6d7575
5935             break;
5936         case TrackElemType::RightEighthToDiag:
5937         case TrackElemType::RightEighthToOrthogonal:
5938             lateralFactor = -137;
5939             // 6d759C
5940             break;
5941         case TrackElemType::LeftEighthBankToDiag:
5942         case TrackElemType::LeftEighthBankToOrthogonal:
5943             vertFactor = 270;
5944             lateralFactor = 200;
5945             // 6d75C3
5946             break;
5947         case TrackElemType::RightEighthBankToDiag:
5948         case TrackElemType::RightEighthBankToOrthogonal:
5949             vertFactor = 270;
5950             lateralFactor = -200;
5951             // 6d75D2
5952             break;
5953         case TrackElemType::DiagFlatToUp25:
5954         case TrackElemType::DiagDown25ToFlat:
5955         case TrackElemType::DiagLeftBankToUp25:
5956         case TrackElemType::DiagRightBankToUp25:
5957         case TrackElemType::DiagDown25ToLeftBank:
5958         case TrackElemType::DiagDown25ToRightBank:
5959             vertFactor = 113;
5960             // 6d7494
5961             break;
5962         case TrackElemType::DiagUp25ToFlat:
5963         case TrackElemType::DiagFlatToDown25:
5964         case TrackElemType::DiagUp25ToLeftBank:
5965         case TrackElemType::DiagUp25ToRightBank:
5966         case TrackElemType::DiagLeftBankToDown25:
5967         case TrackElemType::DiagRightBankToDown25:
5968             vertFactor = -113;
5969             // 6d755D
5970             break;
5971         case TrackElemType::DiagUp25ToUp60:
5972         case TrackElemType::DiagDown60ToDown25:
5973             vertFactor = 95;
5974             // 6D752D
5975             break;
5976         case TrackElemType::DiagUp60ToUp25:
5977         case TrackElemType::DiagDown25ToDown60:
5978             vertFactor = -95;
5979             // 6D7539
5980             break;
5981         case TrackElemType::DiagFlatToUp60:
5982         case TrackElemType::DiagDown60ToFlat:
5983             vertFactor = 60;
5984             // 6D7464
5985             break;
5986         case TrackElemType::DiagUp60ToFlat:
5987         case TrackElemType::DiagFlatToDown60:
5988             vertFactor = -60;
5989             // 6d7470
5990             break;
5991         case TrackElemType::LeftBarrelRollUpToDown:
5992         case TrackElemType::LeftBarrelRollDownToUp:
5993             vertFactor = 170;
5994             lateralFactor = 115;
5995             // 6d7581
5996             break;
5997         case TrackElemType::RightBarrelRollUpToDown:
5998         case TrackElemType::RightBarrelRollDownToUp:
5999             vertFactor = 170;
6000             lateralFactor = -115;
6001             // 6d75A8
6002             break;
6003         case TrackElemType::LeftBankToLeftQuarterTurn3TilesUp25:
6004             vertFactor = -(track_progress / 2) + 134;
6005             lateralFactor = 90;
6006             // 6d771C
6007             break;
6008         case TrackElemType::RightBankToRightQuarterTurn3TilesUp25:
6009             vertFactor = -(track_progress / 2) + 134;
6010             lateralFactor = -90;
6011             // 6D7746
6012             break;
6013         case TrackElemType::LeftQuarterTurn3TilesDown25ToLeftBank:
6014             vertFactor = -(track_progress / 2) + 134;
6015             lateralFactor = 90;
6016             // 6D7731 identical to 6d771c
6017             break;
6018         case TrackElemType::RightQuarterTurn3TilesDown25ToRightBank:
6019             vertFactor = -(track_progress / 2) + 134;
6020             lateralFactor = -90;
6021             // 6D775B identical to 6d7746
6022             break;
6023         case TrackElemType::LeftLargeHalfLoopUp:
6024         case TrackElemType::RightLargeHalfLoopUp:
6025             vertFactor = ((static_cast<uint16_t>(-(track_progress - 311))) / 4) + 46;
6026             // 6d7666
6027             break;
6028         case TrackElemType::RightLargeHalfLoopDown:
6029         case TrackElemType::LeftLargeHalfLoopDown:
6030             vertFactor = (track_progress / 4) + 46;
6031             // 6d767F
6032             break;
6033         case TrackElemType::HeartLineTransferUp:
6034             vertFactor = 103;
6035             if (track_progress < 32)
6036                 break;
6037             vertFactor = -103;
6038             if (track_progress < 64)
6039                 break;
6040             vertFactor = 0;
6041             if (track_progress < 96)
6042                 break;
6043             vertFactor = 103;
6044             if (track_progress < 128)
6045                 break;
6046             vertFactor = -103;
6047             // 6d74A0
6048             break;
6049         case TrackElemType::HeartLineTransferDown:
6050             vertFactor = -103;
6051             if (track_progress < 32)
6052                 break;
6053             vertFactor = 103;
6054             if (track_progress < 64)
6055                 break;
6056             vertFactor = 0;
6057             if (track_progress < 96)
6058                 break;
6059             vertFactor = -103;
6060             if (track_progress < 128)
6061                 break;
6062             vertFactor = 103;
6063             // 6D74CA
6064             break;
6065         case TrackElemType::MultiDimInvertedFlatToDown90QuarterLoop:
6066         case TrackElemType::InvertedFlatToDown90QuarterLoop:
6067         case TrackElemType::MultiDimFlatToDown90QuarterLoop:
6068             vertFactor = (track_progress / 4) + 55;
6069             // 6d762D
6070             break;
6071         case TrackElemType::Up90ToInvertedFlatQuarterLoop:
6072         case TrackElemType::MultiDimUp90ToInvertedFlatQuarterLoop:
6073         case TrackElemType::MultiDimInvertedUp90ToFlatQuarterLoop:
6074             vertFactor = ((static_cast<uint16_t>(-(track_progress - 137))) / 4) + 55;
6075             // 6D7614
6076             break;
6077         case TrackElemType::AirThrustTopCap:
6078             vertFactor = -60;
6079             // 6D744C
6080             break;
6081         case TrackElemType::LeftBankedQuarterTurn3TileUp25:
6082         case TrackElemType::LeftBankedQuarterTurn3TileDown25:
6083             vertFactor = 200;
6084             lateralFactor = 100;
6085             // 6d76C8
6086             break;
6087         case TrackElemType::RightBankedQuarterTurn3TileUp25:
6088         case TrackElemType::RightBankedQuarterTurn3TileDown25:
6089             vertFactor = 200;
6090             lateralFactor = -100;
6091             // 6d76d7
6092             break;
6093         case TrackElemType::LeftBankedQuarterTurn5TileUp25:
6094         case TrackElemType::LeftBankedQuarterTurn5TileDown25:
6095             vertFactor = 200;
6096             lateralFactor = 160;
6097             // 6D76E6
6098             break;
6099         case TrackElemType::RightBankedQuarterTurn5TileUp25:
6100         case TrackElemType::RightBankedQuarterTurn5TileDown25:
6101             vertFactor = 200;
6102             lateralFactor = -160;
6103             // 6d76F5
6104             break;
6105     }
6106 
6107     int32_t gForceLateral = 0;
6108 
6109     if (vertFactor != 0)
6110     {
6111         gForceVert += abs(velocity) * 98 / vertFactor;
6112     }
6113 
6114     if (lateralFactor != 0)
6115     {
6116         gForceLateral += abs(velocity) * 98 / lateralFactor;
6117     }
6118 
6119     gForceVert *= 10;
6120     gForceLateral *= 10;
6121     gForceVert >>= 16;
6122     gForceLateral >>= 16;
6123     return { static_cast<int16_t>(gForceVert & 0xFFFF), static_cast<int16_t>(gForceLateral & 0xFFFF) };
6124 }
6125 
SetMapToolbar() const6126 void Vehicle::SetMapToolbar() const
6127 {
6128     auto curRide = GetRide();
6129     if (curRide != nullptr && curRide->type < RIDE_TYPE_COUNT)
6130     {
6131         const Vehicle* vehicle = GetHead();
6132 
6133         int32_t vehicleIndex;
6134         for (vehicleIndex = 0; vehicleIndex < 32; vehicleIndex++)
6135             if (curRide->vehicles[vehicleIndex] == vehicle->sprite_index)
6136                 break;
6137 
6138         auto ft = Formatter();
6139         ft.Add<rct_string_id>(STR_RIDE_MAP_TIP);
6140         ft.Add<rct_string_id>(STR_MAP_TOOLTIP_STRINGID_STRINGID);
6141         curRide->FormatNameTo(ft);
6142         ft.Add<rct_string_id>(GetRideComponentName(GetRideTypeDescriptor(curRide->type).NameConvention.vehicle).capitalised);
6143         ft.Add<uint16_t>(vehicleIndex + 1);
6144         curRide->FormatStatusTo(ft);
6145         auto intent = Intent(INTENT_ACTION_SET_MAP_TOOLTIP);
6146         intent.putExtra(INTENT_EXTRA_FORMATTER, &ft);
6147         context_broadcast_intent(&intent);
6148     }
6149 }
6150 
TrainHead() const6151 Vehicle* Vehicle::TrainHead() const
6152 {
6153     const Vehicle* vehicle = this;
6154     Vehicle* prevVehicle;
6155 
6156     for (;;)
6157     {
6158         prevVehicle = GetEntity<Vehicle>(vehicle->prev_vehicle_on_ride);
6159         if (prevVehicle == nullptr)
6160             return nullptr;
6161         if (prevVehicle->next_vehicle_on_train == SPRITE_INDEX_NULL)
6162             break;
6163 
6164         vehicle = prevVehicle;
6165     }
6166 
6167     return const_cast<Vehicle*>(vehicle);
6168 }
6169 
TrainTail() const6170 Vehicle* Vehicle::TrainTail() const
6171 {
6172     const Vehicle* vehicle = this;
6173     uint16_t spriteIndex;
6174 
6175     while ((spriteIndex = vehicle->next_vehicle_on_train) != SPRITE_INDEX_NULL)
6176     {
6177         vehicle = GetEntity<Vehicle>(spriteIndex);
6178         if (vehicle == nullptr)
6179         {
6180             return const_cast<Vehicle*>(this);
6181         }
6182     }
6183 
6184     return const_cast<Vehicle*>(vehicle);
6185 }
6186 
IsUsedInPairs() const6187 int32_t Vehicle::IsUsedInPairs() const
6188 {
6189     return num_seats & VEHICLE_SEAT_PAIR_FLAG;
6190 }
6191 
6192 /**
6193  *
6194  *  rct2: 0x006DA44E
6195  */
UpdateMotionDodgems()6196 int32_t Vehicle::UpdateMotionDodgems()
6197 {
6198     _vehicleMotionTrackFlags = 0;
6199 
6200     auto curRide = GetRide();
6201     if (curRide == nullptr)
6202         return _vehicleMotionTrackFlags;
6203 
6204     int32_t nextVelocity = velocity + acceleration;
6205     if (curRide->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN)
6206         && curRide->breakdown_reason_pending == BREAKDOWN_SAFETY_CUT_OUT)
6207     {
6208         nextVelocity = 0;
6209     }
6210     velocity = nextVelocity;
6211 
6212     _vehicleVelocityF64E08 = nextVelocity;
6213     _vehicleVelocityF64E0C = (nextVelocity / 1024) * 42;
6214     _vehicleUnkF64E10 = 1;
6215 
6216     acceleration = 0;
6217     if (!(curRide->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN))
6218         || curRide->breakdown_reason_pending != BREAKDOWN_SAFETY_CUT_OUT)
6219     {
6220         if (gCurrentTicks & 1 && var_34 != 0)
6221         {
6222             if (var_34 > 0)
6223             {
6224                 var_34--;
6225                 sprite_direction += 2;
6226             }
6227             else
6228             {
6229                 var_34++;
6230                 sprite_direction -= 2;
6231             }
6232             sprite_direction &= 0x1E;
6233             Invalidate();
6234         }
6235         else if ((scenario_rand() & 0xFFFF) <= 2849)
6236         {
6237             if (var_35 & (1 << 6))
6238                 sprite_direction -= 2;
6239             else
6240                 sprite_direction += 2;
6241             sprite_direction &= 0x1E;
6242             Invalidate();
6243         }
6244     }
6245 
6246     uint16_t collideSprite = SPRITE_INDEX_NULL;
6247 
6248     if (dodgems_collision_direction != 0)
6249     {
6250         uint8_t oldCollisionDirection = dodgems_collision_direction & 0x1E;
6251         dodgems_collision_direction = 0;
6252 
6253         CoordsXYZ location = { x, y, z };
6254 
6255         location.x += Unk9A36C4[oldCollisionDirection].x;
6256         location.y += Unk9A36C4[oldCollisionDirection].y;
6257         location.x += Unk9A36C4[oldCollisionDirection + 1].x;
6258         location.y += Unk9A36C4[oldCollisionDirection + 1].y;
6259 
6260         if (!DodgemsCarWouldCollideAt(location, &collideSprite))
6261         {
6262             MoveTo(location);
6263         }
6264     }
6265 
6266     remaining_distance += _vehicleVelocityF64E0C;
6267 
6268     if (remaining_distance >= 13962)
6269     {
6270         sound2_flags &= ~VEHICLE_SOUND2_FLAGS_LIFT_HILL;
6271         unk_F64E20.x = x;
6272         unk_F64E20.y = y;
6273         unk_F64E20.z = z;
6274 
6275         while (true)
6276         {
6277             var_35++;
6278             uint8_t direction = sprite_direction;
6279             direction |= var_35 & 1;
6280 
6281             CoordsXY location = unk_F64E20;
6282             location.x += Unk9A36C4[direction].x;
6283             location.y += Unk9A36C4[direction].y;
6284 
6285             if (DodgemsCarWouldCollideAt(location, &collideSprite))
6286                 break;
6287 
6288             remaining_distance -= Unk9A36C4[direction].distance;
6289             unk_F64E20.x = location.x;
6290             unk_F64E20.y = location.y;
6291             if (remaining_distance < 13962)
6292             {
6293                 break;
6294             }
6295             _vehicleUnkF64E10++;
6296         }
6297 
6298         if (remaining_distance >= 13962)
6299         {
6300             int32_t oldVelocity = velocity;
6301             remaining_distance = 0;
6302             velocity = 0;
6303             uint8_t direction = sprite_direction | 1;
6304 
6305             Vehicle* collideVehicle = GetEntity<Vehicle>(collideSprite);
6306             if (collideVehicle != nullptr)
6307             {
6308                 var_34 = (scenario_rand() & 1) ? 1 : -1;
6309 
6310                 if (oldVelocity >= 131072)
6311                 {
6312                     collideVehicle->dodgems_collision_direction = direction;
6313                     dodgems_collision_direction = direction ^ (1 << 4);
6314                 }
6315             }
6316             else
6317             {
6318                 var_34 = (scenario_rand() & 1) ? 6 : -6;
6319 
6320                 if (oldVelocity >= 131072)
6321                 {
6322                     dodgems_collision_direction = direction ^ (1 << 4);
6323                 }
6324             }
6325         }
6326 
6327         MoveTo(unk_F64E20);
6328     }
6329 
6330     int32_t eax = velocity / 2;
6331     int32_t edx = velocity >> 8;
6332     edx *= edx;
6333     if (velocity < 0)
6334         edx = -edx;
6335     edx >>= 5;
6336     eax += edx;
6337     if (mass != 0)
6338     {
6339         eax /= mass;
6340     }
6341     rct_ride_entry* rideEntry = GetRideEntry();
6342     rct_ride_entry_vehicle* vehicleEntry = &rideEntry->vehicles[vehicle_type];
6343 
6344     if (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_POWERED))
6345     {
6346         acceleration = -eax;
6347         return _vehicleMotionTrackFlags;
6348     }
6349 
6350     int32_t momentum = (speed * mass) >> 2;
6351     int32_t _eax = speed << 14;
6352     if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_REVERSING_SHUTTLE))
6353     {
6354         _eax = -_eax;
6355     }
6356     _eax -= velocity;
6357     _eax *= powered_acceleration * 2;
6358     if (momentum != 0)
6359         _eax /= momentum;
6360 
6361     acceleration = _eax - eax;
6362     return _vehicleMotionTrackFlags;
6363 }
6364 
6365 /**
6366  *
6367  *  rct2: 0x006DD365
6368  */
wouldCollideWithDodgemsTrackEdge(const CoordsXY & coords,const CoordsXY & trackLocation,uint32_t trackType,uint16_t dodgemsCarRadius)6369 static bool wouldCollideWithDodgemsTrackEdge(
6370     const CoordsXY& coords, const CoordsXY& trackLocation, uint32_t trackType, uint16_t dodgemsCarRadius)
6371 {
6372     int16_t rideLeft = trackLocation.x + DodgemsTrackSize(trackType).left;
6373     int16_t rideRight = trackLocation.x + DodgemsTrackSize(trackType).right;
6374     int16_t rideTop = trackLocation.y + DodgemsTrackSize(trackType).top;
6375     int16_t rideBottom = trackLocation.y + DodgemsTrackSize(trackType).bottom;
6376 
6377     return coords.x - dodgemsCarRadius < rideLeft || coords.y - dodgemsCarRadius < rideTop
6378         || coords.x + dodgemsCarRadius > rideRight || coords.y + dodgemsCarRadius > rideBottom;
6379 }
6380 
DodgemsCarWouldCollideAt(const CoordsXY & coords,uint16_t * collidedWith) const6381 bool Vehicle::DodgemsCarWouldCollideAt(const CoordsXY& coords, uint16_t* collidedWith) const
6382 {
6383     auto trackType = GetTrackType();
6384 
6385     if (wouldCollideWithDodgemsTrackEdge(coords, TrackLocation, trackType, (var_44 * 30) >> 9))
6386     {
6387         if (collidedWith != nullptr)
6388             *collidedWith = SPRITE_INDEX_NULL;
6389         return true;
6390     }
6391 
6392     auto location = coords;
6393 
6394     ride_id_t rideIndex = ride;
6395     for (auto xy_offset : SurroundingTiles)
6396     {
6397         location += xy_offset;
6398 
6399         for (auto vehicle2 : EntityTileList<Vehicle>(location))
6400         {
6401             if (vehicle2 == this)
6402                 continue;
6403             if (vehicle2->ride != rideIndex)
6404                 continue;
6405 
6406             int32_t distX = abs(coords.x - vehicle2->x);
6407             if (distX > 32768)
6408                 continue;
6409 
6410             int32_t distY = abs(coords.y - vehicle2->y);
6411             if (distY > 32768)
6412                 continue;
6413 
6414             int32_t ecx = (var_44 + vehicle2->var_44) / 2;
6415             ecx *= 30;
6416             ecx >>= 8;
6417             if (std::max(distX, distY) < ecx)
6418             {
6419                 if (collidedWith != nullptr)
6420                     *collidedWith = vehicle2->sprite_index;
6421                 return true;
6422             }
6423         }
6424     }
6425 
6426     return false;
6427 }
6428 
6429 /**
6430  *
6431  *  rct2: 0x006DAB90
6432  */
UpdateTrackMotionUpStopCheck() const6433 void Vehicle::UpdateTrackMotionUpStopCheck() const
6434 {
6435     auto vehicleEntry = Entry();
6436     if (vehicleEntry == nullptr)
6437     {
6438         return;
6439     }
6440 
6441     // No up stops (coaster types)
6442     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_NO_UPSTOP_WHEELS)
6443     {
6444         auto trackType = GetTrackType();
6445         if (!track_element_is_covered(trackType))
6446         {
6447             auto gForces = GetGForces();
6448             gForces.LateralG = std::abs(gForces.LateralG);
6449             if (gForces.LateralG <= 150)
6450             {
6451                 if (dword_9A2970[Pitch] < 0)
6452                 {
6453                     if (gForces.VerticalG > -40)
6454                     {
6455                         return;
6456                     }
6457                 }
6458                 else if (gForces.VerticalG > -80)
6459                 {
6460                     return;
6461                 }
6462             }
6463 
6464             if (Pitch != 8)
6465             {
6466                 _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_DERAILED;
6467             }
6468         }
6469     }
6470     else if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_NO_UPSTOP_BOBSLEIGH)
6471     {
6472         // No up stops bobsleigh type
6473         auto trackType = GetTrackType();
6474         if (!track_element_is_covered(trackType))
6475         {
6476             auto gForces = GetGForces();
6477 
6478             if (dword_9A2970[Pitch] < 0)
6479             {
6480                 if (gForces.VerticalG > -45)
6481                 {
6482                     return;
6483                 }
6484             }
6485             else
6486             {
6487                 if (gForces.VerticalG > -80)
6488                 {
6489                     return;
6490                 }
6491             }
6492 
6493             if (Pitch != 8 && Pitch != 55)
6494             {
6495                 _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_DERAILED;
6496             }
6497         }
6498     }
6499 }
6500 
6501 /**
6502  * Modifies the train's velocity to match the block-brake fixed velocity.
6503  * This function must be called when the car is running through a non-stopping
6504  * state block-brake (precondition), which means that the block brake is acting
6505  * merely as a velocity regulator, in a closed state. When the brake is open, it
6506  * boosts the train to the speed limit
6507  */
ApplyNonStopBlockBrake()6508 void Vehicle::ApplyNonStopBlockBrake()
6509 {
6510     if (velocity >= 0)
6511     {
6512         // If the vehicle is below the speed limit
6513         if (velocity <= BLOCK_BRAKE_BASE_SPEED)
6514         {
6515             // Boost it to the fixed block brake speed
6516             velocity = BLOCK_BRAKE_BASE_SPEED;
6517             acceleration = 0;
6518         }
6519         else
6520         {
6521             // Slow it down till the fixed block brake speed
6522             velocity -= velocity >> 4;
6523             acceleration = 0;
6524         }
6525     }
6526 }
6527 
6528 /**
6529  *
6530  * Modifies the train's velocity influenced by a block brake
6531  */
ApplyStopBlockBrake()6532 void Vehicle::ApplyStopBlockBrake()
6533 {
6534     // Slow it down till completely stop the car
6535     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_BLOCK_BRAKE;
6536     acceleration = 0;
6537     // If the this is slow enough, stop it. If not, slow it down
6538     if (velocity <= 0x20000)
6539     {
6540         velocity = 0;
6541     }
6542     else
6543     {
6544         velocity -= velocity >> 3;
6545     }
6546 }
6547 
6548 /**
6549  *
6550  *  rct2: 0x006DAC43
6551  */
CheckAndApplyBlockSectionStopSite()6552 void Vehicle::CheckAndApplyBlockSectionStopSite()
6553 {
6554     auto curRide = GetRide();
6555     if (curRide == nullptr)
6556         return;
6557 
6558     auto vehicleEntry = Entry();
6559     if (vehicleEntry == nullptr)
6560         return;
6561 
6562     // Is chair lift type
6563     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_CHAIRLIFT)
6564     {
6565         velocity = _vehicleBreakdown == 0 ? 0 : curRide->speed << 16;
6566         acceleration = 0;
6567     }
6568 
6569     auto trackType = GetTrackType();
6570 
6571     TileElement* trackElement = map_get_track_element_at_of_type(TrackLocation, trackType);
6572 
6573     if (trackElement == nullptr)
6574     {
6575         return;
6576     }
6577 
6578     switch (trackType)
6579     {
6580         case TrackElemType::BlockBrakes:
6581             if (curRide->IsBlockSectioned() && trackElement->AsTrack()->BlockBrakeClosed())
6582                 ApplyStopBlockBrake();
6583             else
6584                 ApplyNonStopBlockBrake();
6585 
6586             break;
6587         case TrackElemType::EndStation:
6588             if (trackElement->AsTrack()->BlockBrakeClosed())
6589                 _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_BLOCK_BRAKE;
6590 
6591             break;
6592         case TrackElemType::Up25ToFlat:
6593         case TrackElemType::Up60ToFlat:
6594         case TrackElemType::CableLiftHill:
6595         case TrackElemType::DiagUp25ToFlat:
6596         case TrackElemType::DiagUp60ToFlat:
6597             if (curRide->IsBlockSectioned())
6598             {
6599                 if (trackType == TrackElemType::CableLiftHill || trackElement->AsTrack()->HasChain())
6600                 {
6601                     if (trackElement->AsTrack()->BlockBrakeClosed())
6602                     {
6603                         ApplyStopBlockBrake();
6604                     }
6605                 }
6606             }
6607             break;
6608     }
6609 }
6610 
6611 /**
6612  *
6613  *  rct2: 0x006DADAE
6614  */
UpdateVelocity()6615 void Vehicle::UpdateVelocity()
6616 {
6617     int32_t nextVelocity = acceleration + velocity;
6618     if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_ZERO_VELOCITY))
6619     {
6620         nextVelocity = 0;
6621     }
6622     if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_ON_BRAKE_FOR_DROP))
6623     {
6624         vertical_drop_countdown--;
6625         if (vertical_drop_countdown == -70)
6626         {
6627             ClearUpdateFlag(VEHICLE_UPDATE_FLAG_ON_BRAKE_FOR_DROP);
6628         }
6629         if (vertical_drop_countdown >= 0)
6630         {
6631             nextVelocity = 0;
6632             acceleration = 0;
6633         }
6634     }
6635     velocity = nextVelocity;
6636 
6637     _vehicleVelocityF64E08 = nextVelocity;
6638     _vehicleVelocityF64E0C = (nextVelocity >> 10) * 42;
6639 }
6640 
block_brakes_open_previous_section(Ride & ride,const CoordsXYZ & vehicleTrackLocation,TileElement * tileElement)6641 static void block_brakes_open_previous_section(Ride& ride, const CoordsXYZ& vehicleTrackLocation, TileElement* tileElement)
6642 {
6643     auto location = vehicleTrackLocation;
6644     track_begin_end trackBeginEnd, slowTrackBeginEnd;
6645     TileElement slowTileElement = *tileElement;
6646     bool counter = true;
6647     CoordsXY slowLocation = location;
6648     do
6649     {
6650         if (!track_block_get_previous({ location, tileElement }, &trackBeginEnd))
6651         {
6652             return;
6653         }
6654         if (trackBeginEnd.begin_x == vehicleTrackLocation.x && trackBeginEnd.begin_y == vehicleTrackLocation.y
6655             && tileElement == trackBeginEnd.begin_element)
6656         {
6657             return;
6658         }
6659 
6660         location.x = trackBeginEnd.end_x;
6661         location.y = trackBeginEnd.end_y;
6662         location.z = trackBeginEnd.begin_z;
6663         tileElement = trackBeginEnd.begin_element;
6664 
6665         //#2081: prevent infinite loop
6666         counter = !counter;
6667         if (counter)
6668         {
6669             track_block_get_previous({ slowLocation, &slowTileElement }, &slowTrackBeginEnd);
6670             slowLocation.x = slowTrackBeginEnd.end_x;
6671             slowLocation.y = slowTrackBeginEnd.end_y;
6672             slowTileElement = *(slowTrackBeginEnd.begin_element);
6673             if (slowLocation == location && slowTileElement.GetBaseZ() == tileElement->GetBaseZ()
6674                 && slowTileElement.GetType() == tileElement->GetType()
6675                 && slowTileElement.GetDirection() == tileElement->GetDirection())
6676             {
6677                 return;
6678             }
6679         }
6680     } while (!(trackBeginEnd.begin_element->AsTrack()->IsBlockStart()));
6681 
6682     // Get the start of the track block instead of the end
6683     location = { trackBeginEnd.begin_x, trackBeginEnd.begin_y, trackBeginEnd.begin_z };
6684     auto trackElement = map_get_track_element_at(location);
6685     if (trackElement == nullptr)
6686     {
6687         return;
6688     }
6689     trackElement->SetBlockBrakeClosed(false);
6690     map_invalidate_element(location, reinterpret_cast<TileElement*>(trackElement));
6691 
6692     auto trackType = trackElement->GetTrackType();
6693     if (trackType == TrackElemType::BlockBrakes || trackType == TrackElemType::EndStation)
6694     {
6695         if (ride.IsBlockSectioned())
6696         {
6697             OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::BlockBrakeClose, location);
6698         }
6699     }
6700 }
6701 
GetSwingAmount() const6702 int32_t Vehicle::GetSwingAmount() const
6703 {
6704     auto trackType = GetTrackType();
6705     switch (trackType)
6706     {
6707         case TrackElemType::LeftQuarterTurn5Tiles:
6708         case TrackElemType::BankedLeftQuarterTurn5Tiles:
6709         case TrackElemType::LeftQuarterTurn5TilesUp25:
6710         case TrackElemType::LeftQuarterTurn5TilesDown25:
6711         case TrackElemType::LeftQuarterTurn5TilesCovered:
6712         case TrackElemType::LeftHalfBankedHelixUpLarge:
6713         case TrackElemType::LeftHalfBankedHelixDownLarge:
6714         case TrackElemType::LeftQuarterBankedHelixLargeUp:
6715         case TrackElemType::LeftQuarterBankedHelixLargeDown:
6716         case TrackElemType::LeftQuarterHelixLargeUp:
6717         case TrackElemType::LeftQuarterHelixLargeDown:
6718         case TrackElemType::LeftBankedQuarterTurn5TileUp25:
6719         case TrackElemType::LeftBankedQuarterTurn5TileDown25:
6720             // loc_6D67E1
6721             return 14;
6722 
6723         case TrackElemType::RightQuarterTurn5Tiles:
6724         case TrackElemType::BankedRightQuarterTurn5Tiles:
6725         case TrackElemType::RightQuarterTurn5TilesUp25:
6726         case TrackElemType::RightQuarterTurn5TilesDown25:
6727         case TrackElemType::RightQuarterTurn5TilesCovered:
6728         case TrackElemType::RightHalfBankedHelixUpLarge:
6729         case TrackElemType::RightHalfBankedHelixDownLarge:
6730         case TrackElemType::RightQuarterBankedHelixLargeUp:
6731         case TrackElemType::RightQuarterBankedHelixLargeDown:
6732         case TrackElemType::RightQuarterHelixLargeUp:
6733         case TrackElemType::RightQuarterHelixLargeDown:
6734         case TrackElemType::RightBankedQuarterTurn5TileUp25:
6735         case TrackElemType::RightBankedQuarterTurn5TileDown25:
6736             // loc_6D6804
6737             return -14;
6738 
6739         case TrackElemType::SBendLeft:
6740         case TrackElemType::SBendLeftCovered:
6741             // loc_6D67EF
6742             if (track_progress < 48)
6743             {
6744                 return 14;
6745             }
6746             return -15;
6747 
6748         case TrackElemType::SBendRight:
6749         case TrackElemType::SBendRightCovered:
6750             // loc_6D67CC
6751             if (track_progress < 48)
6752             {
6753                 return -14;
6754             }
6755             return 15;
6756 
6757         case TrackElemType::LeftQuarterTurn3Tiles:
6758         case TrackElemType::LeftBankedQuarterTurn3Tiles:
6759         case TrackElemType::LeftQuarterTurn3TilesUp25:
6760         case TrackElemType::LeftQuarterTurn3TilesDown25:
6761         case TrackElemType::LeftQuarterTurn3TilesCovered:
6762         case TrackElemType::LeftHalfBankedHelixUpSmall:
6763         case TrackElemType::LeftHalfBankedHelixDownSmall:
6764         case TrackElemType::LeftBankToLeftQuarterTurn3TilesUp25:
6765         case TrackElemType::LeftQuarterTurn3TilesDown25ToLeftBank:
6766         case TrackElemType::LeftCurvedLiftHill:
6767         case TrackElemType::LeftBankedQuarterTurn3TileUp25:
6768         case TrackElemType::LeftBankedQuarterTurn3TileDown25:
6769             // loc_6D67BE
6770             return 13;
6771 
6772         case TrackElemType::RightQuarterTurn3Tiles:
6773         case TrackElemType::RightBankedQuarterTurn3Tiles:
6774         case TrackElemType::RightQuarterTurn3TilesUp25:
6775         case TrackElemType::RightQuarterTurn3TilesDown25:
6776         case TrackElemType::RightQuarterTurn3TilesCovered:
6777         case TrackElemType::RightHalfBankedHelixUpSmall:
6778         case TrackElemType::RightHalfBankedHelixDownSmall:
6779         case TrackElemType::RightBankToRightQuarterTurn3TilesUp25:
6780         case TrackElemType::RightQuarterTurn3TilesDown25ToRightBank:
6781         case TrackElemType::RightCurvedLiftHill:
6782         case TrackElemType::RightBankedQuarterTurn3TileUp25:
6783         case TrackElemType::RightBankedQuarterTurn3TileDown25:
6784             // loc_6D67B0
6785             return -13;
6786 
6787         case TrackElemType::LeftQuarterTurn1Tile:
6788         case TrackElemType::LeftQuarterTurn1TileUp60:
6789         case TrackElemType::LeftQuarterTurn1TileDown60:
6790             // loc_6D67A2
6791             return 12;
6792 
6793         case TrackElemType::RightQuarterTurn1Tile:
6794         case TrackElemType::RightQuarterTurn1TileUp60:
6795         case TrackElemType::RightQuarterTurn1TileDown60:
6796             // loc_6D6794
6797             return -12;
6798 
6799         case TrackElemType::LeftEighthToDiag:
6800         case TrackElemType::LeftEighthToOrthogonal:
6801         case TrackElemType::LeftEighthBankToDiag:
6802         case TrackElemType::LeftEighthBankToOrthogonal:
6803             // loc_6D67D3
6804             return 15;
6805 
6806         case TrackElemType::RightEighthToDiag:
6807         case TrackElemType::RightEighthToOrthogonal:
6808         case TrackElemType::RightEighthBankToDiag:
6809         case TrackElemType::RightEighthBankToOrthogonal:
6810             // loc_6D67F6
6811             return -15;
6812     }
6813     return 0;
6814 }
6815 
GetSwingSprite(int16_t swingPosition)6816 static uint8_t GetSwingSprite(int16_t swingPosition)
6817 {
6818     if (swingPosition < -10012)
6819         return 11;
6820     if (swingPosition > 10012)
6821         return 12;
6822 
6823     if (swingPosition < -8191)
6824         return 9;
6825     if (swingPosition > 8191)
6826         return 10;
6827 
6828     if (swingPosition < -6371)
6829         return 7;
6830     if (swingPosition > 6371)
6831         return 8;
6832 
6833     if (swingPosition < -4550)
6834         return 5;
6835     if (swingPosition > 4550)
6836         return 6;
6837 
6838     if (swingPosition < -2730)
6839         return 3;
6840     if (swingPosition > 2730)
6841         return 4;
6842 
6843     if (swingPosition < -910)
6844         return 1;
6845     if (swingPosition > 910)
6846         return 2;
6847 
6848     return 0;
6849 }
6850 
6851 /**
6852  *
6853  *  rct2: 0x006D6776
6854  */
UpdateSwingingCar()6855 void Vehicle::UpdateSwingingCar()
6856 {
6857     int32_t dword_F64E08 = abs(_vehicleVelocityF64E08);
6858     SwingSpeed += (-SwingPosition) >> 6;
6859     int32_t swingAmount = GetSwingAmount();
6860     if (swingAmount < 0)
6861     {
6862         SwingSpeed -= dword_F64E08 >> (-swingAmount);
6863     }
6864     else if (swingAmount > 0)
6865     {
6866         SwingSpeed += dword_F64E08 >> swingAmount;
6867     }
6868 
6869     auto vehicleEntry = Entry();
6870     if (vehicleEntry == nullptr)
6871     {
6872         return;
6873     }
6874     int16_t dx = 3185;
6875     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_SUSPENDED_SWING)
6876     {
6877         dx = 5006;
6878     }
6879     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_WOODEN_WILD_MOUSE_SWING)
6880     {
6881         dx = 1820;
6882     }
6883     int16_t cx = -dx;
6884 
6885     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_SLIDE_SWING)
6886     {
6887         dx = 5370;
6888         cx = -5370;
6889 
6890         auto trackType = GetTrackType();
6891         switch (trackType)
6892         {
6893             case TrackElemType::BankedLeftQuarterTurn5Tiles:
6894             case TrackElemType::LeftBank:
6895             case TrackElemType::LeftBankedQuarterTurn3Tiles:
6896                 dx = 10831;
6897                 cx = -819;
6898                 break;
6899             case TrackElemType::BankedRightQuarterTurn5Tiles:
6900             case TrackElemType::RightBank:
6901             case TrackElemType::RightBankedQuarterTurn3Tiles:
6902                 dx = 819;
6903                 cx = -10831;
6904                 break;
6905         }
6906 
6907         if (track_type_is_station(trackType) || trackType == TrackElemType::Brakes || trackType == TrackElemType::BlockBrakes)
6908         {
6909             dx = 0;
6910             cx = 0;
6911         }
6912 
6913         if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL))
6914         {
6915             dx = 0;
6916             cx = 0;
6917         }
6918     }
6919 
6920     SwingPosition += SwingSpeed;
6921     SwingSpeed -= SwingSpeed >> 5;
6922 
6923     if (SwingPosition > dx)
6924     {
6925         SwingPosition = dx;
6926         SwingSpeed = 0;
6927     }
6928     if (SwingPosition < cx)
6929     {
6930         SwingPosition = cx;
6931         SwingSpeed = 0;
6932     }
6933 
6934     uint8_t swingSprite = GetSwingSprite(SwingPosition);
6935 
6936     if (swingSprite != SwingSprite)
6937     {
6938         SwingSprite = swingSprite;
6939         Invalidate();
6940     }
6941 }
6942 
6943 /**
6944  *
6945  *  rct2: 0x006D661F
6946  */
UpdateSpinningCar()6947 void Vehicle::UpdateSpinningCar()
6948 {
6949     if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_ROTATION_OFF_WILD_MOUSE))
6950     {
6951         spin_speed = 0;
6952         return;
6953     }
6954 
6955     auto vehicleEntry = Entry();
6956     if (vehicleEntry == nullptr)
6957     {
6958         return;
6959     }
6960     int32_t spinningInertia = vehicleEntry->spinning_inertia;
6961     auto trackType = GetTrackType();
6962     int32_t dword_F64E08 = _vehicleVelocityF64E08;
6963     int32_t spinSpeed{};
6964     // An L spin adds to the spin speed, R does the opposite
6965     // The number indicates how much right shift of the velocity will become spin
6966     // The bigger the number the less change in spin.
6967 
6968     const auto& ted = GetTrackElementDescriptor(trackType);
6969     switch (ted.SpinFunction)
6970     {
6971         case RC_SPIN:
6972             // On a rotation control track element
6973             spinningInertia += 6;
6974             spinSpeed = dword_F64E08 >> spinningInertia;
6975             // Alternate the spin direction (roughly). Perhaps in future save a value to the track
6976             if (sprite_index & 1)
6977             {
6978                 spin_speed -= spinSpeed;
6979             }
6980             else
6981             {
6982                 spin_speed += spinSpeed;
6983             }
6984             break;
6985         case R5_SPIN:
6986             // It looks like in the original there was going to be special code for whirlpool
6987             // this has been removed and just uses R5_SPIN
6988             spinningInertia += 5;
6989             spin_speed -= dword_F64E08 >> spinningInertia;
6990             break;
6991         case L5_SPIN:
6992             spinningInertia += 5;
6993             spin_speed += dword_F64E08 >> spinningInertia;
6994             break;
6995         case R7_SPIN:
6996             spinningInertia += 7;
6997             spin_speed -= dword_F64E08 >> spinningInertia;
6998             break;
6999         case L7_SPIN:
7000             spinningInertia += 7;
7001             spin_speed += dword_F64E08 >> spinningInertia;
7002             break;
7003         case RL_SPIN:
7004             // Right Left Curve Track Piece
7005             if (track_progress < 48)
7006             {
7007                 // R8_SPIN
7008                 spinningInertia += 8;
7009                 spin_speed -= dword_F64E08 >> spinningInertia;
7010                 break;
7011             }
7012             [[fallthrough]];
7013         case L9_SPIN:
7014             spinningInertia += 9;
7015             spin_speed += dword_F64E08 >> spinningInertia;
7016             break;
7017         case L8_SPIN:
7018             spinningInertia += 8;
7019             spin_speed += dword_F64E08 >> spinningInertia;
7020             break;
7021         case SP_SPIN:
7022             // On rapids spin after fully on them
7023             if (track_progress > 22)
7024             {
7025                 // L5_SPIN
7026                 spinningInertia += 5;
7027                 spin_speed += dword_F64E08 >> spinningInertia;
7028             }
7029             break;
7030         case LR_SPIN:
7031             // Left Right Curve Track Piece
7032             if (track_progress < 48)
7033             {
7034                 // L8_SPIN
7035                 spinningInertia += 8;
7036                 spin_speed += dword_F64E08 >> spinningInertia;
7037                 break;
7038             }
7039             [[fallthrough]];
7040         case R9_SPIN:
7041             spinningInertia += 9;
7042             spin_speed -= dword_F64E08 >> spinningInertia;
7043             break;
7044         case R8_SPIN:
7045             spinningInertia += 8;
7046             spin_speed -= dword_F64E08 >> spinningInertia;
7047             break;
7048     }
7049 
7050     spinSpeed = std::clamp(spin_speed, VEHICLE_MIN_SPIN_SPEED, VEHICLE_MAX_SPIN_SPEED);
7051     spin_speed = spinSpeed;
7052     spin_sprite += spinSpeed >> 8;
7053     // Note this actually increases the spin speed if going right!
7054     spin_speed -= spinSpeed >> vehicleEntry->spinning_friction;
7055     Invalidate();
7056 }
7057 
UpdateAnimationAnimalFlying()7058 void Vehicle::UpdateAnimationAnimalFlying()
7059 {
7060     if (animationState > 0)
7061     {
7062         animationState--;
7063         return;
7064     }
7065 
7066     if (animation_frame == 0)
7067     {
7068         auto trackType = GetTrackType();
7069         TileElement* trackElement = map_get_track_element_at_of_type_seq(TrackLocation, trackType, 0);
7070         if (trackElement != nullptr && trackElement->AsTrack()->HasChain())
7071         {
7072             // start flapping, bird
7073             animation_frame = 1;
7074             animationState = 5;
7075             Invalidate();
7076         }
7077     }
7078     else
7079     {
7080         // continue flapping until reaching frame 0
7081         animation_frame = (animation_frame + 1) % 4;
7082         Invalidate();
7083     }
7084     // number of frames to skip before updating again
7085     constexpr std::array frameWaitTimes = { 5, 3, 5, 3 };
7086     animationState = frameWaitTimes[animation_frame];
7087 }
7088 
7089 /**
7090  *
7091  *  rct2: 0x006D63D4
7092  */
UpdateAdditionalAnimation()7093 void Vehicle::UpdateAdditionalAnimation()
7094 {
7095     uint8_t targetFrame{};
7096     uint8_t curFrame{};
7097     uint32_t eax{};
7098 
7099     auto vehicleEntry = Entry();
7100     if (vehicleEntry == nullptr)
7101     {
7102         return;
7103     }
7104     switch (vehicleEntry->animation)
7105     {
7106         case VEHICLE_ENTRY_ANIMATION_MINITURE_RAILWAY_LOCOMOTIVE: // loc_6D652B
7107             animationState += _vehicleVelocityF64E08;
7108             targetFrame = (animationState >> 20) & 3;
7109             if (animation_frame != targetFrame)
7110             {
7111                 curFrame = animation_frame;
7112                 animation_frame = targetFrame;
7113                 targetFrame &= 0x02;
7114                 curFrame &= 0x02;
7115                 if (targetFrame != curFrame)
7116                 {
7117                     auto curRide = GetRide();
7118                     if (curRide != nullptr)
7119                     {
7120                         if (!ride_has_station_shelter(curRide)
7121                             || (status != Vehicle::Status::MovingToEndOfStation && status != Vehicle::Status::Arriving))
7122                         {
7123                             int32_t typeIndex = [&] {
7124                                 switch (Pitch)
7125                                 {
7126                                     case 2:
7127                                         // uphill
7128                                         return 1;
7129                                     case 6:
7130                                         // downhill
7131                                         return 2;
7132                                     default:
7133                                         return 0;
7134                                 }
7135                             }();
7136                             int32_t directionIndex = sprite_direction >> 1;
7137                             auto offset = SteamParticleOffsets[typeIndex][directionIndex];
7138                             SteamParticle::Create({ x + offset.x, y + offset.y, z + offset.z });
7139                         }
7140                     }
7141                 }
7142                 Invalidate();
7143             }
7144             break;
7145         case VEHICLE_ENTRY_ANIMATION_SWAN: // loc_6D6424
7146             animationState += _vehicleVelocityF64E08;
7147             targetFrame = (animationState >> 18) & 2;
7148             if (animation_frame != targetFrame)
7149             {
7150                 animation_frame = targetFrame;
7151                 Invalidate();
7152             }
7153             break;
7154         case VEHICLE_ENTRY_ANIMATION_CANOES: // loc_6D6482
7155             animationState += _vehicleVelocityF64E08;
7156             eax = ((animationState >> 13) & 0xFF) * 6;
7157             targetFrame = (eax >> 8) & 0xFF;
7158             if (animation_frame != targetFrame)
7159             {
7160                 animation_frame = targetFrame;
7161                 Invalidate();
7162             }
7163             break;
7164         case VEHICLE_ENTRY_ANIMATION_ROW_BOATS: // loc_6D64F7
7165             animationState += _vehicleVelocityF64E08;
7166             eax = ((animationState >> 13) & 0xFF) * 7;
7167             targetFrame = (eax >> 8) & 0xFF;
7168             if (animation_frame != targetFrame)
7169             {
7170                 animation_frame = targetFrame;
7171                 Invalidate();
7172             }
7173             break;
7174         case VEHICLE_ENTRY_ANIMATION_WATER_TRICYCLES: // loc_6D6453
7175             animationState += _vehicleVelocityF64E08;
7176             targetFrame = (animationState >> 19) & 1;
7177             if (animation_frame != targetFrame)
7178             {
7179                 animation_frame = targetFrame;
7180                 Invalidate();
7181             }
7182             break;
7183         case VEHICLE_ENTRY_ANIMATION_OBSERVATION_TOWER: // loc_6D65C3
7184             if (animationState <= 0xCCCC)
7185             {
7186                 animationState += 0x3333;
7187             }
7188             else
7189             {
7190                 animationState = 0;
7191                 animation_frame += 1;
7192                 animation_frame &= 7;
7193                 Invalidate();
7194             }
7195             break;
7196         case VEHICLE_ENTRY_ANIMATION_HELICARS: // loc_6D63F5
7197             animationState += _vehicleVelocityF64E08;
7198             targetFrame = (animationState >> 18) & 3;
7199             if (animation_frame != targetFrame)
7200             {
7201                 animation_frame = targetFrame;
7202                 Invalidate();
7203             }
7204             break;
7205         case VEHICLE_ENTRY_ANIMATION_MONORAIL_CYCLES: // loc_6D64B6
7206             if (num_peeps != 0)
7207             {
7208                 animationState += _vehicleVelocityF64E08;
7209                 eax = ((animationState >> 13) & 0xFF) << 2;
7210                 targetFrame = (eax >> 8) & 0xFF;
7211                 if (animation_frame != targetFrame)
7212                 {
7213                     animation_frame = targetFrame;
7214                     Invalidate();
7215                 }
7216             }
7217             break;
7218         case VEHICLE_ENTRY_ANIMATION_MULTI_DIM_COASTER: // loc_6D65E1
7219             if (seat_rotation != target_seat_rotation)
7220             {
7221                 if (animationState <= 0xCCCC)
7222                 {
7223                     animationState += 0x3333;
7224                 }
7225                 else
7226                 {
7227                     animationState = 0;
7228 
7229                     if (seat_rotation >= target_seat_rotation)
7230                         seat_rotation--;
7231 
7232                     else
7233                         seat_rotation++;
7234 
7235                     animation_frame = (seat_rotation - 4) & 7;
7236                     Invalidate();
7237                 }
7238             }
7239             break;
7240         case VEHICLE_ENTRY_ANIMATION_ANIMAL_FLYING:
7241             UpdateAnimationAnimalFlying();
7242             // makes animation play faster with vehicle speed
7243             targetFrame = abs(_vehicleVelocityF64E08) >> 24;
7244             animationState = std::max(animationState - targetFrame, 0u);
7245             break;
7246     }
7247 }
7248 
7249 /**
7250  *
7251  *  rct2: 0x006DEDB1
7252  */
play_scenery_door_open_sound(const CoordsXYZ & loc,WallElement * tileElement)7253 static void play_scenery_door_open_sound(const CoordsXYZ& loc, WallElement* tileElement)
7254 {
7255     auto* wallEntry = tileElement->GetEntry();
7256     int32_t doorSoundType = wall_entry_get_door_sound(wallEntry);
7257     if (doorSoundType != 0)
7258     {
7259         auto soundId = DoorOpenSoundIds[doorSoundType - 1];
7260         if (soundId != OpenRCT2::Audio::SoundId::Null)
7261         {
7262             OpenRCT2::Audio::Play3D(soundId, loc);
7263         }
7264     }
7265 }
7266 
7267 /**
7268  *
7269  *  rct2: 0x006DED7A
7270  */
play_scenery_door_close_sound(const CoordsXYZ & loc,WallElement * tileElement)7271 static void play_scenery_door_close_sound(const CoordsXYZ& loc, WallElement* tileElement)
7272 {
7273     auto* wallEntry = tileElement->GetEntry();
7274     int32_t doorSoundType = wall_entry_get_door_sound(wallEntry);
7275     if (doorSoundType != 0)
7276     {
7277         auto soundId = DoorCloseSoundIds[doorSoundType - 1];
7278         if (soundId != OpenRCT2::Audio::SoundId::Null)
7279         {
7280             Play3D(soundId, loc);
7281         }
7282     }
7283 }
7284 
7285 template<bool isBackwards>
AnimateSceneryDoor(const CoordsXYZD & doorLocation,const CoordsXYZ & trackLocation,bool isLastVehicle)7286 static void AnimateSceneryDoor(const CoordsXYZD& doorLocation, const CoordsXYZ& trackLocation, bool isLastVehicle)
7287 {
7288     auto door = map_get_wall_element_at(doorLocation);
7289     if (door == nullptr)
7290     {
7291         return;
7292     }
7293 
7294     if (!isLastVehicle && (door->GetAnimationFrame() == 0))
7295     {
7296         door->SetAnimationIsBackwards(isBackwards);
7297         door->SetAnimationFrame(1);
7298         map_animation_create(MAP_ANIMATION_TYPE_WALL_DOOR, doorLocation);
7299         play_scenery_door_open_sound(trackLocation, door);
7300     }
7301 
7302     if (isLastVehicle)
7303     {
7304         door->SetAnimationIsBackwards(isBackwards);
7305         door->SetAnimationFrame(6);
7306         play_scenery_door_close_sound(trackLocation, door);
7307     }
7308 }
7309 
7310 /**
7311  *
7312  *  rct2: 0x006DEE93
7313  */
UpdateSceneryDoor() const7314 void Vehicle::UpdateSceneryDoor() const
7315 {
7316     auto trackType = GetTrackType();
7317     const auto& ted = GetTrackElementDescriptor(trackType);
7318     const rct_preview_track* trackBlock = ted.Block;
7319     while ((trackBlock + 1)->index != 255)
7320     {
7321         trackBlock++;
7322     }
7323     const rct_track_coordinates* trackCoordinates = &ted.Coordinates;
7324     auto wallCoords = CoordsXYZ{ x, y, TrackLocation.z - trackBlock->z + trackCoordinates->z_end }.ToTileStart();
7325     int32_t direction = (GetTrackDirection() + trackCoordinates->rotation_end) & 3;
7326 
7327     AnimateSceneryDoor<false>(
7328         { wallCoords, static_cast<Direction>(direction) }, TrackLocation, next_vehicle_on_train == SPRITE_INDEX_NULL);
7329 }
7330 
AnimateLandscapeDoor(TrackElement * trackElement,bool isLastVehicle)7331 template<bool isBackwards> static void AnimateLandscapeDoor(TrackElement* trackElement, bool isLastVehicle)
7332 {
7333     auto doorState = isBackwards ? trackElement->GetDoorAState() : trackElement->GetDoorBState();
7334     if (!isLastVehicle && doorState == LANDSCAPE_DOOR_CLOSED)
7335     {
7336         if (isBackwards)
7337             trackElement->SetDoorAState(LANDSCAPE_DOOR_OPEN);
7338         else
7339             trackElement->SetDoorBState(LANDSCAPE_DOOR_OPEN);
7340         // TODO: play door open sound
7341     }
7342 
7343     if (isLastVehicle)
7344     {
7345         if (isBackwards)
7346             trackElement->SetDoorAState(LANDSCAPE_DOOR_CLOSED);
7347         else
7348             trackElement->SetDoorBState(LANDSCAPE_DOOR_CLOSED);
7349         // TODO: play door close sound
7350     }
7351 }
7352 
UpdateLandscapeDoor() const7353 void Vehicle::UpdateLandscapeDoor() const
7354 {
7355     const auto* currentRide = GetRide();
7356     if (currentRide == nullptr || !currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS))
7357     {
7358         return;
7359     }
7360 
7361     auto coords = CoordsXYZ{ x, y, TrackLocation.z }.ToTileStart();
7362     auto* tileElement = map_get_track_element_at_from_ride(coords, ride);
7363     if (tileElement != nullptr && tileElement->GetType() == static_cast<uint8_t>(TileElementType::Track))
7364     {
7365         AnimateLandscapeDoor<false>(tileElement->AsTrack(), next_vehicle_on_train == SPRITE_INDEX_NULL);
7366     }
7367 }
7368 
7369 /**
7370  *
7371  *  rct2: 0x006DB38B
7372  */
PitchAndRollStart(bool useInvertedSprites,TileElement * tileElement)7373 static PitchAndRoll PitchAndRollStart(bool useInvertedSprites, TileElement* tileElement)
7374 {
7375     auto trackType = tileElement->AsTrack()->GetTrackType();
7376     const auto& ted = GetTrackElementDescriptor(trackType);
7377     return PitchAndRoll{ ted.Definition.vangle_start, track_get_actual_bank_3(useInvertedSprites, tileElement) };
7378 }
7379 
UpdateGoKartAttemptSwitchLanes()7380 void Vehicle::UpdateGoKartAttemptSwitchLanes()
7381 {
7382     uint16_t probability = 0x8000;
7383     if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_6))
7384     {
7385         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_6);
7386     }
7387     else
7388     {
7389         probability = 0x0A3D;
7390     }
7391     if ((scenario_rand() & 0xFFFF) <= probability)
7392     {
7393         // This changes "riding left" to "moving to right lane" and "riding right" to "moving to left lane".
7394         TrackSubposition = VehicleTrackSubposition{ static_cast<uint8_t>(static_cast<uint8_t>(TrackSubposition) + 2U) };
7395     }
7396 }
7397 
7398 /**
7399  *
7400  *  rct2: 0x006DB545
7401  */
trigger_on_ride_photo(const CoordsXYZ & loc,TileElement * tileElement)7402 static void trigger_on_ride_photo(const CoordsXYZ& loc, TileElement* tileElement)
7403 {
7404     tileElement->AsTrack()->SetPhotoTimeout();
7405 
7406     map_animation_create(MAP_ANIMATION_TYPE_TRACK_ONRIDEPHOTO, { loc, tileElement->GetBaseZ() });
7407 }
7408 
7409 /**
7410  *
7411  *  rct2: 0x006DEDE8
7412  */
UpdateSceneryDoorBackwards() const7413 void Vehicle::UpdateSceneryDoorBackwards() const
7414 {
7415     auto trackType = GetTrackType();
7416     const auto& ted = GetTrackElementDescriptor(trackType);
7417     const rct_preview_track* trackBlock = ted.Block;
7418     const rct_track_coordinates* trackCoordinates = &ted.Coordinates;
7419     auto wallCoords = CoordsXYZ{ TrackLocation, TrackLocation.z - trackBlock->z + trackCoordinates->z_begin };
7420     int32_t direction = (GetTrackDirection() + trackCoordinates->rotation_begin) & 3;
7421     direction = direction_reverse(direction);
7422 
7423     AnimateSceneryDoor<true>(
7424         { wallCoords, static_cast<Direction>(direction) }, TrackLocation, next_vehicle_on_train == SPRITE_INDEX_NULL);
7425 }
7426 
UpdateLandscapeDoorBackwards() const7427 void Vehicle::UpdateLandscapeDoorBackwards() const
7428 {
7429     const auto* currentRide = GetRide();
7430     if (currentRide == nullptr || !currentRide->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS))
7431     {
7432         return;
7433     }
7434 
7435     auto coords = CoordsXYZ{ TrackLocation, TrackLocation.z };
7436     auto* tileElement = map_get_track_element_at_from_ride(coords, ride);
7437     if (tileElement != nullptr && tileElement->GetType() == static_cast<uint8_t>(TileElementType::Track))
7438     {
7439         AnimateLandscapeDoor<true>(tileElement->AsTrack(), next_vehicle_on_train == SPRITE_INDEX_NULL);
7440     }
7441 }
7442 
vehicle_update_play_water_splash_sound()7443 static void vehicle_update_play_water_splash_sound()
7444 {
7445     if (_vehicleVelocityF64E08 <= BLOCK_BRAKE_BASE_SPEED)
7446     {
7447         return;
7448     }
7449 
7450     OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::WaterSplash, { unk_F64E20.x, unk_F64E20.y, unk_F64E20.z });
7451 }
7452 
7453 /**
7454  *
7455  *  rct2: 0x006DB59E
7456  */
UpdateHandleWaterSplash() const7457 void Vehicle::UpdateHandleWaterSplash() const
7458 {
7459     rct_ride_entry* rideEntry = GetRideEntry();
7460     auto trackType = GetTrackType();
7461 
7462     if (!(rideEntry->flags & RIDE_ENTRY_FLAG_PLAY_SPLASH_SOUND))
7463     {
7464         if (rideEntry->flags & RIDE_ENTRY_FLAG_PLAY_SPLASH_SOUND_SLIDE)
7465         {
7466             if (IsHead())
7467             {
7468                 if (track_element_is_covered(trackType))
7469                 {
7470                     Vehicle* nextVehicle = GetEntity<Vehicle>(next_vehicle_on_ride);
7471                     if (nextVehicle == nullptr)
7472                         return;
7473 
7474                     Vehicle* nextNextVehicle = GetEntity<Vehicle>(nextVehicle->next_vehicle_on_ride);
7475                     if (nextNextVehicle == nullptr)
7476                         return;
7477                     if (!track_element_is_covered(nextNextVehicle->GetTrackType()))
7478                     {
7479                         if (track_progress == 4)
7480                         {
7481                             vehicle_update_play_water_splash_sound();
7482                         }
7483                     }
7484                 }
7485             }
7486         }
7487     }
7488     else
7489     {
7490         if (trackType == TrackElemType::Down25ToFlat)
7491         {
7492             if (track_progress == 12)
7493             {
7494                 vehicle_update_play_water_splash_sound();
7495             }
7496         }
7497     }
7498     if (IsHead())
7499     {
7500         if (trackType == TrackElemType::Watersplash)
7501         {
7502             if (track_progress == 48)
7503             {
7504                 vehicle_update_play_water_splash_sound();
7505             }
7506         }
7507     }
7508 }
7509 
7510 /**
7511  *
7512  *  rct2: 0x006DB807
7513  */
UpdateReverserCarBogies()7514 void Vehicle::UpdateReverserCarBogies()
7515 {
7516     const auto moveInfo = GetMoveInfo();
7517     MoveTo({ TrackLocation.x + moveInfo->x, TrackLocation.y + moveInfo->y, z });
7518 }
7519 
7520 /**
7521  * Collision Detection
7522  *  rct2: 0x006DD078
7523  * @param vehicle (esi)
7524  * @param x (ax)
7525  * @param y (cx)
7526  * @param z (dx)
7527  * @param otherVehicleIndex (bp)
7528  */
UpdateMotionCollisionDetection(const CoordsXYZ & loc,uint16_t * otherVehicleIndex)7529 bool Vehicle::UpdateMotionCollisionDetection(const CoordsXYZ& loc, uint16_t* otherVehicleIndex)
7530 {
7531     if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_COLLISION_DISABLED))
7532         return false;
7533 
7534     auto vehicleEntry = Entry();
7535     if (vehicleEntry == nullptr)
7536     {
7537         return false;
7538     }
7539 
7540     if (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_BOAT_HIRE_COLLISION_DETECTION))
7541     {
7542         var_C4 = 0;
7543 
7544         // If hacking boat hire rides you can end up here
7545         if (otherVehicleIndex == nullptr)
7546             return false;
7547 
7548         Vehicle* collideVehicle = GetEntity<Vehicle>(*otherVehicleIndex);
7549         if (collideVehicle == nullptr)
7550             return false;
7551 
7552         if (this == collideVehicle)
7553             return false;
7554 
7555         int32_t x_diff = abs(loc.x - collideVehicle->x);
7556         if (x_diff > 0x7FFF)
7557             return false;
7558 
7559         int32_t y_diff = abs(loc.y - collideVehicle->y);
7560         if (y_diff > 0x7FFF)
7561             return false;
7562 
7563         int32_t z_diff = abs(loc.z - collideVehicle->z);
7564         if (x_diff + y_diff + z_diff > 0xFFFF)
7565             return false;
7566 
7567         uint16_t ecx = std::min(var_44 + collideVehicle->var_44, 560);
7568         ecx = ((ecx >> 1) * 30) >> 8;
7569 
7570         if (x_diff + y_diff + z_diff >= ecx)
7571             return false;
7572 
7573         uint8_t direction = (sprite_direction - collideVehicle->sprite_direction + 7) & 0x1F;
7574         return direction < 0xF;
7575     }
7576 
7577     CoordsXY location = loc;
7578 
7579     bool mayCollide = false;
7580     Vehicle* collideVehicle = nullptr;
7581     for (auto xy_offset : SurroundingTiles)
7582     {
7583         location += xy_offset;
7584 
7585         for (auto vehicle2 : EntityTileList<Vehicle>(location))
7586         {
7587             if (vehicle2 == this)
7588                 continue;
7589 
7590             int32_t z_diff = abs(vehicle2->z - loc.z);
7591 
7592             if (z_diff > 16)
7593                 continue;
7594 
7595             if (vehicle2->ride_subtype == OBJECT_ENTRY_INDEX_NULL)
7596                 continue;
7597 
7598             auto collideVehicleEntry = vehicle2->Entry();
7599             if (collideVehicleEntry == nullptr)
7600                 continue;
7601 
7602             if (!(collideVehicleEntry->flags & VEHICLE_ENTRY_FLAG_BOAT_HIRE_COLLISION_DETECTION))
7603                 continue;
7604 
7605             uint32_t x_diff = abs(vehicle2->x - loc.x);
7606             if (x_diff > 0x7FFF)
7607                 continue;
7608 
7609             uint32_t y_diff = abs(vehicle2->y - loc.y);
7610             if (y_diff > 0x7FFF)
7611                 continue;
7612 
7613             VehicleTrackSubposition cl = std::min(TrackSubposition, vehicle2->TrackSubposition);
7614             VehicleTrackSubposition ch = std::max(TrackSubposition, vehicle2->TrackSubposition);
7615             if (cl != ch)
7616             {
7617                 if (cl == VehicleTrackSubposition::GoKartsLeftLane && ch == VehicleTrackSubposition::GoKartsRightLane)
7618                     continue;
7619             }
7620 
7621             uint32_t ecx = var_44 + vehicle2->var_44;
7622             ecx = ((ecx >> 1) * 30) >> 8;
7623 
7624             if (x_diff + y_diff >= ecx)
7625                 continue;
7626 
7627             if (!(collideVehicleEntry->flags & VEHICLE_ENTRY_FLAG_GO_KART))
7628             {
7629                 collideVehicle = vehicle2;
7630                 mayCollide = true;
7631                 break;
7632             }
7633 
7634             uint8_t direction = (sprite_direction - vehicle2->sprite_direction - 6) & 0x1F;
7635 
7636             if (direction < 0x14)
7637                 continue;
7638 
7639             uint32_t offsetSpriteDirection = (sprite_direction + 4) & 31;
7640             uint32_t offsetDirection = offsetSpriteDirection >> 3;
7641             uint32_t next_x_diff = abs(loc.x + AvoidCollisionMoveOffset[offsetDirection].x - vehicle2->x);
7642             uint32_t next_y_diff = abs(loc.y + AvoidCollisionMoveOffset[offsetDirection].y - vehicle2->y);
7643 
7644             if (next_x_diff + next_y_diff < x_diff + y_diff)
7645             {
7646                 collideVehicle = vehicle2;
7647                 mayCollide = true;
7648                 break;
7649             }
7650         }
7651         if (mayCollide)
7652         {
7653             break;
7654         }
7655     }
7656 
7657     if (!mayCollide)
7658     {
7659         var_C4 = 0;
7660         return false;
7661     }
7662 
7663     var_C4++;
7664     if (var_C4 < 200)
7665     {
7666         SetUpdateFlag(VEHICLE_UPDATE_FLAG_6);
7667         if (otherVehicleIndex != nullptr)
7668             *otherVehicleIndex = collideVehicle->sprite_index;
7669         return true;
7670     }
7671 
7672     // TODO Is it possible for collideVehicle to be NULL?
7673 
7674     if (status == Vehicle::Status::MovingToEndOfStation)
7675     {
7676         if (sprite_direction == 0)
7677         {
7678             if (x <= collideVehicle->x)
7679             {
7680                 return false;
7681             }
7682         }
7683         else if (sprite_direction == 8)
7684         {
7685             if (y >= collideVehicle->y)
7686             {
7687                 return false;
7688             }
7689         }
7690         else if (sprite_direction == 16)
7691         {
7692             if (x >= collideVehicle->x)
7693             {
7694                 return false;
7695             }
7696         }
7697         else if (sprite_direction == 24)
7698         {
7699             if (y <= collideVehicle->y)
7700             {
7701                 return false;
7702             }
7703         }
7704     }
7705 
7706     if (collideVehicle->status == Vehicle::Status::TravellingBoat && status != Vehicle::Status::Arriving
7707         && status != Vehicle::Status::Travelling)
7708     {
7709         return false;
7710     }
7711 
7712     SetUpdateFlag(VEHICLE_UPDATE_FLAG_6);
7713     if (otherVehicleIndex != nullptr)
7714         *otherVehicleIndex = collideVehicle->sprite_index;
7715     return true;
7716 }
7717 
7718 /**
7719  *
7720  *  rct2: 0x006DB7D6
7721  */
ReverseReverserCar()7722 void Vehicle::ReverseReverserCar()
7723 {
7724     Vehicle* previousVehicle = GetEntity<Vehicle>(prev_vehicle_on_ride);
7725     Vehicle* nextVehicle = GetEntity<Vehicle>(next_vehicle_on_ride);
7726     if (previousVehicle == nullptr || nextVehicle == nullptr)
7727     {
7728         return;
7729     }
7730 
7731     track_progress = 168;
7732     vehicle_type ^= 1;
7733 
7734     previousVehicle->track_progress = 86;
7735     nextVehicle->track_progress = 158;
7736 
7737     nextVehicle->UpdateReverserCarBogies();
7738     previousVehicle->UpdateReverserCarBogies();
7739 }
7740 
7741 /**
7742  *
7743  *  rct2: 0x006DBF3E
7744  */
Sub6DBF3E()7745 void Vehicle::Sub6DBF3E()
7746 {
7747     rct_ride_entry_vehicle* vehicleEntry = Entry();
7748 
7749     acceleration /= _vehicleUnkF64E10;
7750     if (TrackSubposition == VehicleTrackSubposition::ChairliftGoingBack)
7751     {
7752         return;
7753     }
7754 
7755     auto trackType = GetTrackType();
7756     const auto& ted = GetTrackElementDescriptor(trackType);
7757     if (!(ted.SequenceProperties[0] & TRACK_SEQUENCE_FLAG_ORIGIN))
7758     {
7759         return;
7760     }
7761 
7762     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_3;
7763 
7764     TileElement* tileElement = nullptr;
7765     if (map_is_location_valid(TrackLocation))
7766     {
7767         tileElement = map_get_track_element_at_of_type_seq(TrackLocation, trackType, 0);
7768     }
7769 
7770     if (tileElement == nullptr)
7771     {
7772         return;
7773     }
7774 
7775     if (_vehicleStationIndex == STATION_INDEX_NULL)
7776     {
7777         _vehicleStationIndex = tileElement->AsTrack()->GetStationIndex();
7778     }
7779 
7780     if (trackType == TrackElemType::TowerBase && this == gCurrentVehicle)
7781     {
7782         if (track_progress > 3 && !HasUpdateFlag(VEHICLE_UPDATE_FLAG_REVERSING_SHUTTLE))
7783         {
7784             CoordsXYE output;
7785             int32_t outputZ, outputDirection;
7786 
7787             CoordsXYE input = { TrackLocation, tileElement };
7788             if (!track_block_get_next(&input, &output, &outputZ, &outputDirection))
7789             {
7790                 _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_12;
7791             }
7792         }
7793 
7794         if (track_progress <= 3)
7795         {
7796             _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION;
7797         }
7798     }
7799 
7800     if (trackType != TrackElemType::EndStation || this != gCurrentVehicle)
7801     {
7802         return;
7803     }
7804 
7805     uint16_t ax = track_progress;
7806     if (_vehicleVelocityF64E08 < 0)
7807     {
7808         if (ax <= 22)
7809         {
7810             _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION;
7811         }
7812     }
7813     else
7814     {
7815         uint16_t cx = 17;
7816         if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_CHAIRLIFT)
7817         {
7818             cx = 6;
7819         }
7820         if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_GO_KART)
7821         {
7822             // Determine the stop positions for the karts. If in left lane it's further along the track than the right lane.
7823             // Since it's not possible to overtake when the race has ended, this does not check for overtake states (7 and
7824             // 8).
7825             cx = TrackSubposition == VehicleTrackSubposition::GoKartsRightLane ? 18 : 20;
7826         }
7827 
7828         if (ax > cx)
7829         {
7830             _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION;
7831         }
7832     }
7833 }
7834 
7835 /**
7836  *
7837  *  rct2: 0x006DB08C
7838  */
UpdateTrackMotionForwardsGetNewTrack(uint16_t trackType,Ride * curRide,rct_ride_entry * rideEntry)7839 bool Vehicle::UpdateTrackMotionForwardsGetNewTrack(uint16_t trackType, Ride* curRide, rct_ride_entry* rideEntry)
7840 {
7841     CoordsXYZD location = {};
7842 
7843     auto pitchAndRollEnd = TrackPitchAndRollEnd(trackType);
7844     TileElement* tileElement = map_get_track_element_at_of_type_seq(TrackLocation, trackType, 0);
7845 
7846     if (tileElement == nullptr)
7847     {
7848         return false;
7849     }
7850 
7851     if (trackType == TrackElemType::CableLiftHill && this == gCurrentVehicle)
7852     {
7853         _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_11;
7854     }
7855 
7856     if (tileElement->AsTrack()->IsBlockStart())
7857     {
7858         if (next_vehicle_on_train == SPRITE_INDEX_NULL)
7859         {
7860             tileElement->AsTrack()->SetBlockBrakeClosed(true);
7861             if (trackType == TrackElemType::BlockBrakes || trackType == TrackElemType::EndStation)
7862             {
7863                 if (!(rideEntry->vehicles[0].flags & VEHICLE_ENTRY_FLAG_POWERED))
7864                 {
7865                     OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::BlockBrakeRelease, TrackLocation);
7866                 }
7867             }
7868             map_invalidate_element(TrackLocation, tileElement);
7869             block_brakes_open_previous_section(*curRide, TrackLocation, tileElement);
7870         }
7871     }
7872 
7873     // Change from original: this used to check if the vehicle allowed doors.
7874     UpdateSceneryDoor();
7875     UpdateLandscapeDoor();
7876 
7877     bool isGoingBack = false;
7878     switch (TrackSubposition)
7879     {
7880         case VehicleTrackSubposition::ChairliftGoingBack:
7881         case VehicleTrackSubposition::ChairliftEndBullwheel:
7882             TrackSubposition = VehicleTrackSubposition::ChairliftGoingBack;
7883             isGoingBack = true;
7884             break;
7885         case VehicleTrackSubposition::ChairliftStartBullwheel:
7886             TrackSubposition = VehicleTrackSubposition::ChairliftGoingOut;
7887             break;
7888         case VehicleTrackSubposition::GoKartsMovingToRightLane:
7889             TrackSubposition = VehicleTrackSubposition::GoKartsRightLane;
7890             break;
7891         case VehicleTrackSubposition::GoKartsMovingToLeftLane:
7892             TrackSubposition = VehicleTrackSubposition::GoKartsLeftLane;
7893             break;
7894         default:
7895             break;
7896     }
7897 
7898     if (isGoingBack)
7899     {
7900         track_begin_end trackBeginEnd;
7901         if (!track_block_get_previous({ TrackLocation, tileElement }, &trackBeginEnd))
7902         {
7903             return false;
7904         }
7905         location.x = trackBeginEnd.begin_x;
7906         location.y = trackBeginEnd.begin_y;
7907         location.z = trackBeginEnd.begin_z;
7908         location.direction = trackBeginEnd.begin_direction;
7909         tileElement = trackBeginEnd.begin_element;
7910     }
7911     else
7912     {
7913         {
7914             int32_t curZ, direction;
7915             CoordsXYE xyElement = { TrackLocation, tileElement };
7916             if (!track_block_get_next(&xyElement, &xyElement, &curZ, &direction))
7917             {
7918                 return false;
7919             }
7920             tileElement = xyElement.element;
7921             location = { xyElement, curZ, static_cast<Direction>(direction) };
7922         }
7923         if (tileElement->AsTrack()->GetTrackType() == TrackElemType::LeftReverser
7924             || tileElement->AsTrack()->GetTrackType() == TrackElemType::RightReverser)
7925         {
7926             if (IsHead() && velocity <= 0x30000)
7927             {
7928                 velocity = 0;
7929             }
7930         }
7931 
7932         if (PitchAndRollStart(HasUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES), tileElement) != pitchAndRollEnd)
7933         {
7934             return false;
7935         }
7936 
7937         // Update VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES flag
7938         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES);
7939         {
7940             int32_t rideType = get_ride(tileElement->AsTrack()->GetRideIndex())->type;
7941             if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE))
7942             {
7943                 if (tileElement->AsTrack()->IsInverted())
7944                 {
7945                     SetUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES);
7946                 }
7947             }
7948         }
7949     }
7950 
7951     TrackLocation = location;
7952 
7953     // TODO check if getting the vehicle entry again is necessary
7954     rct_ride_entry_vehicle* vehicleEntry = Entry();
7955     if (vehicleEntry == nullptr)
7956     {
7957         return false;
7958     }
7959     if ((vehicleEntry->flags & VEHICLE_ENTRY_FLAG_GO_KART)
7960         && TrackSubposition < VehicleTrackSubposition::GoKartsMovingToRightLane)
7961     {
7962         trackType = tileElement->AsTrack()->GetTrackType();
7963         if (trackType == TrackElemType::Flat
7964             || ((curRide->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING) && tileElement->AsTrack()->IsStation()))
7965         {
7966             UpdateGoKartAttemptSwitchLanes();
7967         }
7968     }
7969 
7970     if (TrackSubposition >= VehicleTrackSubposition::ChairliftGoingOut
7971         && TrackSubposition <= VehicleTrackSubposition::ChairliftStartBullwheel)
7972     {
7973         TileCoordsXYZ curLocation{ TrackLocation };
7974 
7975         if (curLocation == curRide->ChairliftBullwheelLocation[1])
7976         {
7977             TrackSubposition = VehicleTrackSubposition::ChairliftEndBullwheel;
7978         }
7979         else if (curLocation == curRide->ChairliftBullwheelLocation[0])
7980         {
7981             TrackSubposition = VehicleTrackSubposition::ChairliftStartBullwheel;
7982         }
7983     }
7984 
7985     // loc_6DB500
7986     // Update VEHICLE_UPDATE_FLAG_ON_LIFT_HILL
7987     ClearUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL);
7988     if (tileElement->AsTrack()->HasChain())
7989     {
7990         SetUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL);
7991     }
7992 
7993     trackType = tileElement->AsTrack()->GetTrackType();
7994     if (trackType != TrackElemType::Brakes)
7995     {
7996         target_seat_rotation = tileElement->AsTrack()->GetSeatRotation();
7997     }
7998     SetTrackDirection(location.direction);
7999     SetTrackType(trackType);
8000     brake_speed = tileElement->AsTrack()->GetBrakeBoosterSpeed();
8001     if (trackType == TrackElemType::OnRidePhoto)
8002     {
8003         trigger_on_ride_photo(TrackLocation, tileElement);
8004     }
8005     if (trackType == TrackElemType::RotationControlToggle)
8006     {
8007         update_flags ^= VEHICLE_UPDATE_FLAG_ROTATION_OFF_WILD_MOUSE;
8008     }
8009     // Change from original: this used to check if the vehicle allowed doors.
8010     UpdateSceneryDoorBackwards();
8011     UpdateLandscapeDoorBackwards();
8012 
8013     return true;
8014 }
8015 
8016 /**
8017  *
8018  *  rct2: 0x006DAEB9
8019  */
UpdateTrackMotionForwards(rct_ride_entry_vehicle * vehicleEntry,Ride * curRide,rct_ride_entry * rideEntry)8020 bool Vehicle::UpdateTrackMotionForwards(rct_ride_entry_vehicle* vehicleEntry, Ride* curRide, rct_ride_entry* rideEntry)
8021 {
8022     uint16_t otherVehicleIndex = SPRITE_INDEX_NULL;
8023 loc_6DAEB9:
8024     auto trackType = GetTrackType();
8025     if (trackType == TrackElemType::HeartLineTransferUp || trackType == TrackElemType::HeartLineTransferDown)
8026     {
8027         if (track_progress == 80)
8028         {
8029             vehicle_type ^= 1;
8030             vehicleEntry = Entry();
8031         }
8032         if (_vehicleVelocityF64E08 >= 0x40000)
8033         {
8034             acceleration = -_vehicleVelocityF64E08 * 8;
8035         }
8036         else if (_vehicleVelocityF64E08 < 0x20000)
8037         {
8038             acceleration = 0x50000;
8039         }
8040     }
8041     else if (trackType == TrackElemType::Brakes)
8042     {
8043         bool hasBrakesFailure = curRide->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN
8044             && curRide->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE;
8045         if (!hasBrakesFailure || curRide->mechanic_status == RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES)
8046         {
8047             auto brakeSpeed = brake_speed << 16;
8048             if (brakeSpeed < _vehicleVelocityF64E08)
8049             {
8050                 acceleration = -_vehicleVelocityF64E08 * 16;
8051             }
8052             else if (!(gCurrentTicks & 0x0F))
8053             {
8054                 if (_vehicleF64E2C == 0)
8055                 {
8056                     _vehicleF64E2C++;
8057                     OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::BrakeRelease, { x, y, z });
8058                 }
8059             }
8060         }
8061     }
8062     else if (trackType == TrackElemType::Booster)
8063     {
8064         auto boosterSpeed = get_booster_speed(curRide->type, (brake_speed << 16));
8065         if (boosterSpeed > _vehicleVelocityF64E08)
8066         {
8067             acceleration = GetRideTypeDescriptor(curRide->type).OperatingSettings.BoosterAcceleration
8068                 << 16; //_vehicleVelocityF64E08 * 1.2;
8069         }
8070     }
8071 
8072     if ((trackType == TrackElemType::Flat && curRide->type == RIDE_TYPE_REVERSE_FREEFALL_COASTER)
8073         || (trackType == TrackElemType::PoweredLift))
8074     {
8075         acceleration = GetRideTypeDescriptor(curRide->type).OperatingSettings.PoweredLiftAcceleration << 16;
8076     }
8077     if (trackType == TrackElemType::BrakeForDrop)
8078     {
8079         if (IsHead())
8080         {
8081             if (!HasUpdateFlag(VEHICLE_UPDATE_FLAG_ON_BRAKE_FOR_DROP))
8082             {
8083                 if (track_progress >= 8)
8084                 {
8085                     acceleration = -_vehicleVelocityF64E08 * 16;
8086                     if (track_progress >= 24)
8087                     {
8088                         SetUpdateFlag(VEHICLE_UPDATE_FLAG_ON_BRAKE_FOR_DROP);
8089                         vertical_drop_countdown = 90;
8090                     }
8091                 }
8092             }
8093         }
8094     }
8095     if (trackType == TrackElemType::LogFlumeReverser)
8096     {
8097         if (track_progress != 16 || velocity < 0x40000)
8098         {
8099             if (track_progress == 32)
8100             {
8101                 vehicle_type = vehicleEntry->log_flume_reverser_vehicle_type;
8102                 vehicleEntry = Entry();
8103             }
8104         }
8105         else
8106         {
8107             track_progress += 17;
8108         }
8109     }
8110 
8111     uint16_t newTrackProgress = track_progress + 1;
8112 
8113     // Track Total Progress is in the two bytes before the move info list
8114     uint16_t trackTotalProgress = GetTrackProgress();
8115     if (newTrackProgress >= trackTotalProgress)
8116     {
8117         UpdateCrossings();
8118 
8119         if (!UpdateTrackMotionForwardsGetNewTrack(trackType, curRide, rideEntry))
8120         {
8121             _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5;
8122             _vehicleVelocityF64E0C -= remaining_distance + 1;
8123             remaining_distance = -1;
8124             return false;
8125         }
8126         newTrackProgress = 0;
8127     }
8128 
8129     track_progress = newTrackProgress;
8130     UpdateHandleWaterSplash();
8131 
8132     // loc_6DB706
8133     const auto moveInfo = GetMoveInfo();
8134     trackType = GetTrackType();
8135     uint8_t moveInfovehicleSpriteType;
8136     {
8137         auto loc = TrackLocation
8138             + CoordsXYZ{ moveInfo->x, moveInfo->y, moveInfo->z + GetRideTypeDescriptor(curRide->type).Heights.VehicleZOffset };
8139 
8140         uint8_t remainingDistanceFlags = 0;
8141         if (loc.x != unk_F64E20.x)
8142         {
8143             remainingDistanceFlags |= 1;
8144         }
8145         if (loc.y != unk_F64E20.y)
8146         {
8147             remainingDistanceFlags |= 2;
8148         }
8149         if (loc.z != unk_F64E20.z)
8150         {
8151             remainingDistanceFlags |= 4;
8152         }
8153 
8154         if (TrackSubposition == VehicleTrackSubposition::ReverserRCFrontBogie
8155             && (trackType == TrackElemType::LeftReverser || trackType == TrackElemType::RightReverser) && track_progress >= 30
8156             && track_progress <= 66)
8157         {
8158             remainingDistanceFlags |= 8;
8159         }
8160 
8161         if (TrackSubposition == VehicleTrackSubposition::ReverserRCRearBogie
8162             && (trackType == TrackElemType::LeftReverser || trackType == TrackElemType::RightReverser) && track_progress == 96)
8163         {
8164             ReverseReverserCar();
8165 
8166             const rct_vehicle_info* moveInfo2 = GetMoveInfo();
8167             loc.x = x + moveInfo2->x;
8168             loc.y = y + moveInfo2->y;
8169         }
8170 
8171         // loc_6DB8A5
8172         remaining_distance -= dword_9A2930[remainingDistanceFlags];
8173         unk_F64E20 = loc;
8174         sprite_direction = moveInfo->direction;
8175         bank_rotation = moveInfo->bank_rotation;
8176         Pitch = moveInfo->Pitch;
8177 
8178         moveInfovehicleSpriteType = moveInfo->Pitch;
8179 
8180         if ((vehicleEntry->flags & VEHICLE_ENTRY_FLAG_WOODEN_WILD_MOUSE_SWING) && moveInfo->Pitch != 0)
8181         {
8182             SwingSprite = 0;
8183             SwingPosition = 0;
8184             SwingSpeed = 0;
8185         }
8186 
8187         // this == frontVehicle
8188         if (this == _vehicleFrontVehicle)
8189         {
8190             if (_vehicleVelocityF64E08 >= 0)
8191             {
8192                 otherVehicleIndex = prev_vehicle_on_ride;
8193                 if (UpdateMotionCollisionDetection(loc, &otherVehicleIndex))
8194                 {
8195                     _vehicleVelocityF64E0C -= remaining_distance + 1;
8196                     remaining_distance = -1;
8197 
8198                     // Might need to be bp rather than this, but hopefully not
8199                     auto otherVeh = GetEntity<Vehicle>(otherVehicleIndex);
8200                     if (otherVeh == nullptr)
8201                     {
8202                         // This can never happen as prev_vehicle_on_ride will always be set to a vehicle
8203                         log_error("Failed to get next vehicle during update!");
8204                         return true;
8205                     }
8206                     auto head = otherVeh->TrainHead();
8207 
8208                     auto velocityDelta = abs(velocity - head->velocity);
8209                     if (!(rideEntry->flags & RIDE_ENTRY_FLAG_DISABLE_COLLISION_CRASHES))
8210                     {
8211                         if (velocityDelta > 0xE0000)
8212                         {
8213                             if (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_BOAT_HIRE_COLLISION_DETECTION))
8214                             {
8215                                 _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION;
8216                             }
8217                         }
8218                     }
8219 
8220                     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_GO_KART)
8221                     {
8222                         velocity -= velocity >> 2;
8223                     }
8224                     else
8225                     {
8226                         int32_t newHeadVelocity = velocity >> 1;
8227                         velocity = head->velocity >> 1;
8228                         head->velocity = newHeadVelocity;
8229                     }
8230                     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_1;
8231                     return false;
8232                 }
8233             }
8234         }
8235     }
8236 
8237     // loc_6DB928
8238     if (remaining_distance < 0x368A)
8239     {
8240         return true;
8241     }
8242 
8243     acceleration += dword_9A2970[moveInfovehicleSpriteType];
8244     _vehicleUnkF64E10++;
8245     goto loc_6DAEB9;
8246 }
8247 
PitchAndRollEnd(Ride * curRide,bool useInvertedSprites,uint16_t trackType,TileElement * tileElement)8248 static PitchAndRoll PitchAndRollEnd(Ride* curRide, bool useInvertedSprites, uint16_t trackType, TileElement* tileElement)
8249 {
8250     bool isInverted = useInvertedSprites ^ tileElement->AsTrack()->IsInverted();
8251     const auto& ted = GetTrackElementDescriptor(trackType);
8252     return { ted.Definition.vangle_end, track_get_actual_bank_2(curRide->type, isInverted, ted.Definition.bank_end) };
8253 }
8254 
8255 /**
8256  *
8257  *  rct2: 0x006DBAA6
8258  */
UpdateTrackMotionBackwardsGetNewTrack(uint16_t trackType,Ride * curRide,uint16_t * progress)8259 bool Vehicle::UpdateTrackMotionBackwardsGetNewTrack(uint16_t trackType, Ride* curRide, uint16_t* progress)
8260 {
8261     auto pitchAndRollStart = TrackPitchAndRollStart(trackType);
8262     TileElement* tileElement = map_get_track_element_at_of_type_seq(TrackLocation, trackType, 0);
8263 
8264     if (tileElement == nullptr)
8265         return false;
8266 
8267     bool nextTileBackwards = true;
8268     int32_t direction = 0;
8269     // loc_6DBB08:;
8270     auto trackPos = CoordsXYZ{ TrackLocation.x, TrackLocation.y, 0 };
8271 
8272     switch (TrackSubposition)
8273     {
8274         case VehicleTrackSubposition::ChairliftEndBullwheel:
8275             TrackSubposition = VehicleTrackSubposition::ChairliftGoingOut;
8276             break;
8277         case VehicleTrackSubposition::GoKartsMovingToRightLane:
8278             TrackSubposition = VehicleTrackSubposition::GoKartsLeftLane;
8279             break;
8280         case VehicleTrackSubposition::GoKartsMovingToLeftLane:
8281             TrackSubposition = VehicleTrackSubposition::GoKartsRightLane;
8282             break;
8283         case VehicleTrackSubposition::ChairliftGoingBack:
8284         case VehicleTrackSubposition::ChairliftStartBullwheel:
8285             TrackSubposition = VehicleTrackSubposition::ChairliftGoingBack;
8286             nextTileBackwards = false;
8287             break;
8288         default:
8289             break;
8290     }
8291 
8292     if (nextTileBackwards)
8293     {
8294         // loc_6DBB7E:;
8295         track_begin_end trackBeginEnd;
8296         if (!track_block_get_previous({ trackPos, tileElement }, &trackBeginEnd))
8297         {
8298             return false;
8299         }
8300         tileElement = trackBeginEnd.begin_element;
8301 
8302         trackType = tileElement->AsTrack()->GetTrackType();
8303         if (trackType == TrackElemType::LeftReverser || trackType == TrackElemType::RightReverser)
8304         {
8305             return false;
8306         }
8307 
8308         if (PitchAndRollEnd(curRide, HasUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES), trackType, tileElement)
8309             != pitchAndRollStart)
8310         {
8311             return false;
8312         }
8313 
8314         // Update VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES
8315         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES);
8316         if (GetRideTypeDescriptor(curRide->type).HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE))
8317         {
8318             if (tileElement->AsTrack()->IsInverted())
8319             {
8320                 SetUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES);
8321             }
8322         }
8323 
8324         trackPos = { trackBeginEnd.begin_x, trackBeginEnd.begin_y, trackBeginEnd.begin_z };
8325         direction = trackBeginEnd.begin_direction;
8326     }
8327     else
8328     {
8329         // loc_6DBB4F:;
8330         CoordsXYE input;
8331         CoordsXYE output;
8332         int32_t outputZ{};
8333 
8334         input.x = trackPos.x;
8335         input.y = trackPos.y;
8336         input.element = tileElement;
8337         if (!track_block_get_next(&input, &output, &outputZ, &direction))
8338         {
8339             return false;
8340         }
8341         tileElement = output.element;
8342         trackPos = { output, outputZ };
8343     }
8344 
8345     // loc_6DBC3B:
8346     TrackLocation = trackPos;
8347 
8348     if (TrackSubposition >= VehicleTrackSubposition::ChairliftGoingOut
8349         && TrackSubposition <= VehicleTrackSubposition::ChairliftStartBullwheel)
8350     {
8351         TileCoordsXYZ curLocation{ TrackLocation };
8352 
8353         if (curLocation == curRide->ChairliftBullwheelLocation[1])
8354         {
8355             TrackSubposition = VehicleTrackSubposition::ChairliftEndBullwheel;
8356         }
8357         else if (curLocation == curRide->ChairliftBullwheelLocation[0])
8358         {
8359             TrackSubposition = VehicleTrackSubposition::ChairliftStartBullwheel;
8360         }
8361     }
8362 
8363     if (tileElement->AsTrack()->HasChain())
8364     {
8365         if (_vehicleVelocityF64E08 < 0)
8366         {
8367             if (next_vehicle_on_train == SPRITE_INDEX_NULL)
8368             {
8369                 trackType = tileElement->AsTrack()->GetTrackType();
8370                 const auto& ted = GetTrackElementDescriptor(trackType);
8371                 if (!(ted.Flags & TRACK_ELEM_FLAG_DOWN))
8372                 {
8373                     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_9;
8374                 }
8375             }
8376             SetUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL);
8377         }
8378     }
8379     else
8380     {
8381         if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL))
8382         {
8383             ClearUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL);
8384             if (next_vehicle_on_train == SPRITE_INDEX_NULL)
8385             {
8386                 if (_vehicleVelocityF64E08 < 0)
8387                 {
8388                     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_8;
8389                 }
8390             }
8391         }
8392     }
8393 
8394     trackType = tileElement->AsTrack()->GetTrackType();
8395     if (trackType != TrackElemType::Brakes)
8396     {
8397         target_seat_rotation = tileElement->AsTrack()->GetSeatRotation();
8398     }
8399     direction &= 3;
8400     SetTrackType(trackType);
8401     SetTrackDirection(direction);
8402     brake_speed = tileElement->AsTrack()->GetBrakeBoosterSpeed();
8403 
8404     // There are two bytes before the move info list
8405     uint16_t trackTotalProgress = GetTrackProgress();
8406     *progress = trackTotalProgress - 1;
8407     return true;
8408 }
8409 
8410 /**
8411  *
8412  *  rct2: 0x006DBA33
8413  */
UpdateTrackMotionBackwards(rct_ride_entry_vehicle * vehicleEntry,Ride * curRide,rct_ride_entry * rideEntry)8414 bool Vehicle::UpdateTrackMotionBackwards(rct_ride_entry_vehicle* vehicleEntry, Ride* curRide, rct_ride_entry* rideEntry)
8415 {
8416     uint16_t otherVehicleIndex = SPRITE_INDEX_NULL;
8417 
8418     while (true)
8419     {
8420         auto trackType = GetTrackType();
8421         if (trackType == TrackElemType::Flat && curRide->type == RIDE_TYPE_REVERSE_FREEFALL_COASTER)
8422         {
8423             int32_t unkVelocity = _vehicleVelocityF64E08;
8424             if (unkVelocity < -524288)
8425             {
8426                 unkVelocity = abs(unkVelocity);
8427                 acceleration = unkVelocity * 2;
8428             }
8429         }
8430 
8431         if (trackType == TrackElemType::Brakes)
8432         {
8433             if (-(brake_speed << 16) > _vehicleVelocityF64E08)
8434             {
8435                 acceleration = _vehicleVelocityF64E08 * -16;
8436             }
8437         }
8438 
8439         if (trackType == TrackElemType::Booster)
8440         {
8441             auto boosterSpeed = get_booster_speed(curRide->type, (brake_speed << 16));
8442             if (boosterSpeed < _vehicleVelocityF64E08)
8443             {
8444                 acceleration = GetRideTypeDescriptor(curRide->type).OperatingSettings.BoosterAcceleration << 16;
8445             }
8446         }
8447 
8448         uint16_t newTrackProgress = track_progress - 1;
8449         if (newTrackProgress == 0xFFFF)
8450         {
8451             UpdateCrossings();
8452 
8453             if (!UpdateTrackMotionBackwardsGetNewTrack(trackType, curRide, &newTrackProgress))
8454             {
8455                 _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5;
8456                 _vehicleVelocityF64E0C -= remaining_distance - 0x368A;
8457                 remaining_distance = 0x368A;
8458                 return false;
8459             }
8460         }
8461 
8462         // loc_6DBD42
8463         track_progress = newTrackProgress;
8464         uint8_t moveInfoVehicleSpriteType;
8465         {
8466             const rct_vehicle_info* moveInfo = GetMoveInfo();
8467             auto loc = TrackLocation
8468                 + CoordsXYZ{ moveInfo->x, moveInfo->y,
8469                              moveInfo->z + GetRideTypeDescriptor(curRide->type).Heights.VehicleZOffset };
8470 
8471             uint8_t remainingDistanceFlags = 0;
8472             if (loc.x != unk_F64E20.x)
8473             {
8474                 remainingDistanceFlags |= 1;
8475             }
8476             if (loc.y != unk_F64E20.y)
8477             {
8478                 remainingDistanceFlags |= 2;
8479             }
8480             if (loc.z != unk_F64E20.z)
8481             {
8482                 remainingDistanceFlags |= 4;
8483             }
8484             remaining_distance += dword_9A2930[remainingDistanceFlags];
8485 
8486             unk_F64E20 = loc;
8487             sprite_direction = moveInfo->direction;
8488             bank_rotation = moveInfo->bank_rotation;
8489             Pitch = moveInfo->Pitch;
8490             moveInfoVehicleSpriteType = moveInfo->Pitch;
8491 
8492             if ((vehicleEntry->flags & VEHICLE_ENTRY_FLAG_WOODEN_WILD_MOUSE_SWING) && Pitch != 0)
8493             {
8494                 SwingSprite = 0;
8495                 SwingPosition = 0;
8496                 SwingSpeed = 0;
8497             }
8498 
8499             if (this == _vehicleFrontVehicle)
8500             {
8501                 if (_vehicleVelocityF64E08 < 0)
8502                 {
8503                     otherVehicleIndex = next_vehicle_on_ride;
8504                     if (UpdateMotionCollisionDetection(loc, &otherVehicleIndex))
8505                     {
8506                         _vehicleVelocityF64E0C -= remaining_distance - 0x368A;
8507                         remaining_distance = 0x368A;
8508 
8509                         Vehicle* v3 = GetEntity<Vehicle>(otherVehicleIndex);
8510                         Vehicle* v4 = gCurrentVehicle;
8511                         if (v3 == nullptr)
8512                         {
8513                             return false;
8514                         }
8515 
8516                         if (!(rideEntry->flags & RIDE_ENTRY_FLAG_DISABLE_COLLISION_CRASHES))
8517                         {
8518                             if (abs(v4->velocity - v3->velocity) > 0xE0000)
8519                             {
8520                                 if (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_BOAT_HIRE_COLLISION_DETECTION))
8521                                 {
8522                                     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION;
8523                                 }
8524                             }
8525                         }
8526 
8527                         if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_GO_KART)
8528                         {
8529                             velocity -= velocity >> 2;
8530                             _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_2;
8531                         }
8532                         else
8533                         {
8534                             int32_t v3Velocity = v3->velocity;
8535                             v3->velocity = v4->velocity >> 1;
8536                             v4->velocity = v3Velocity >> 1;
8537                             _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_2;
8538                         }
8539 
8540                         return false;
8541                     }
8542                 }
8543             }
8544         }
8545 
8546         // loc_6DBE3F
8547         if (remaining_distance >= 0)
8548         {
8549             return true;
8550         }
8551         acceleration += dword_9A2970[moveInfoVehicleSpriteType];
8552         _vehicleUnkF64E10++;
8553     }
8554 }
8555 
8556 /**
8557  *  rct2: 0x006DC3A7
8558  *
8559  *
8560  */
UpdateTrackMotionMiniGolfVehicle(Ride * curRide,rct_ride_entry * rideEntry,rct_ride_entry_vehicle * vehicleEntry)8561 void Vehicle::UpdateTrackMotionMiniGolfVehicle(Ride* curRide, rct_ride_entry* rideEntry, rct_ride_entry_vehicle* vehicleEntry)
8562 {
8563     uint16_t otherVehicleIndex = SPRITE_INDEX_NULL;
8564     TileElement* tileElement = nullptr;
8565     CoordsXYZ trackPos;
8566     int32_t direction{};
8567 
8568     _vehicleUnkF64E10 = 1;
8569     acceleration = dword_9A2970[Pitch];
8570     if (!HasUpdateFlag(VEHICLE_UPDATE_FLAG_SINGLE_CAR_POSITION))
8571     {
8572         remaining_distance = _vehicleVelocityF64E0C + remaining_distance;
8573     }
8574     if (remaining_distance >= 0 && remaining_distance < 0x368A)
8575     {
8576         goto loc_6DCE02;
8577     }
8578     sound2_flags &= ~VEHICLE_SOUND2_FLAGS_LIFT_HILL;
8579     unk_F64E20.x = x;
8580     unk_F64E20.y = y;
8581     unk_F64E20.z = z;
8582     Invalidate();
8583     if (remaining_distance < 0)
8584         goto loc_6DCA9A;
8585 
8586 loc_6DC462:
8587     if (var_D3 == 0)
8588     {
8589         goto loc_6DC476;
8590     }
8591     var_D3--;
8592     goto loc_6DC985;
8593 
8594 loc_6DC476:
8595     if (mini_golf_flags & MiniGolfFlag::Flag2)
8596     {
8597         uint8_t nextFrame = animation_frame + 1;
8598         if (nextFrame < mini_golf_peep_animation_lengths[EnumValue(mini_golf_current_animation)])
8599         {
8600             animation_frame = nextFrame;
8601             goto loc_6DC985;
8602         }
8603         mini_golf_flags &= ~MiniGolfFlag::Flag2;
8604     }
8605 
8606     if (mini_golf_flags & MiniGolfFlag::Flag0)
8607     {
8608         auto vehicleIdx = IsHead() ? next_vehicle_on_ride : prev_vehicle_on_ride;
8609         Vehicle* vEDI = GetEntity<Vehicle>(vehicleIdx);
8610         if (vEDI == nullptr)
8611         {
8612             return;
8613         }
8614         if (!(vEDI->mini_golf_flags & MiniGolfFlag::Flag0) || (vEDI->mini_golf_flags & MiniGolfFlag::Flag2))
8615         {
8616             goto loc_6DC985;
8617         }
8618         if (vEDI->var_D3 != 0)
8619         {
8620             goto loc_6DC985;
8621         }
8622         vEDI->mini_golf_flags &= ~MiniGolfFlag::Flag0;
8623         mini_golf_flags &= ~MiniGolfFlag::Flag0;
8624     }
8625 
8626     if (mini_golf_flags & MiniGolfFlag::Flag1)
8627     {
8628         auto vehicleIdx = IsHead() ? next_vehicle_on_ride : prev_vehicle_on_ride;
8629         Vehicle* vEDI = GetEntity<Vehicle>(vehicleIdx);
8630         if (vEDI == nullptr)
8631         {
8632             return;
8633         }
8634         if (!(vEDI->mini_golf_flags & MiniGolfFlag::Flag1) || (vEDI->mini_golf_flags & MiniGolfFlag::Flag2))
8635         {
8636             goto loc_6DC985;
8637         }
8638         if (vEDI->var_D3 != 0)
8639         {
8640             goto loc_6DC985;
8641         }
8642         vEDI->mini_golf_flags &= ~MiniGolfFlag::Flag1;
8643         mini_golf_flags &= ~MiniGolfFlag::Flag1;
8644     }
8645 
8646     if (mini_golf_flags & MiniGolfFlag::Flag3)
8647     {
8648         Vehicle* vEDI = this;
8649 
8650         for (;;)
8651         {
8652             vEDI = GetEntity<Vehicle>(vEDI->prev_vehicle_on_ride);
8653             if (vEDI == this || vEDI == nullptr)
8654             {
8655                 break;
8656             }
8657             if (vEDI->IsHead())
8658                 continue;
8659             if (!(vEDI->mini_golf_flags & MiniGolfFlag::Flag4))
8660                 continue;
8661             if (vEDI->TrackLocation != TrackLocation)
8662                 continue;
8663             goto loc_6DC985;
8664         }
8665 
8666         mini_golf_flags |= MiniGolfFlag::Flag4;
8667         mini_golf_flags &= ~MiniGolfFlag::Flag3;
8668     }
8669 
8670     // There are two bytes before the move info list
8671     {
8672         uint16_t trackTotalProgress = GetTrackProgress();
8673         if (track_progress + 1 < trackTotalProgress)
8674         {
8675             track_progress += 1;
8676             goto loc_6DC743;
8677         }
8678     }
8679 
8680     tileElement = map_get_track_element_at_of_type_seq(TrackLocation, GetTrackType(), 0);
8681     {
8682         CoordsXYE output;
8683         int32_t outZ{};
8684         int32_t outDirection{};
8685         CoordsXYE input = { TrackLocation, tileElement };
8686         if (!track_block_get_next(&input, &output, &outZ, &outDirection))
8687         {
8688             goto loc_6DC9BC;
8689         }
8690         tileElement = output.element;
8691         trackPos = { output.x, output.y, outZ };
8692         direction = outDirection;
8693     }
8694 
8695     if (PitchAndRollStart(HasUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES), tileElement)
8696         != TrackPitchAndRollEnd(GetTrackType()))
8697     {
8698         goto loc_6DC9BC;
8699     }
8700 
8701     {
8702         int32_t rideType = get_ride(tileElement->AsTrack()->GetRideIndex())->type;
8703         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES);
8704         if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE))
8705         {
8706             if (tileElement->AsTrack()->IsInverted())
8707             {
8708                 SetUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES);
8709             }
8710         }
8711     }
8712 
8713     TrackLocation = trackPos;
8714 
8715     if (!IsHead())
8716     {
8717         Vehicle* prevVehicle = GetEntity<Vehicle>(prev_vehicle_on_ride);
8718         if (prevVehicle != nullptr)
8719         {
8720             TrackSubposition = prevVehicle->TrackSubposition;
8721         }
8722         if (TrackSubposition != VehicleTrackSubposition::MiniGolfStart9)
8723         {
8724             TrackSubposition = VehicleTrackSubposition{ static_cast<uint8_t>(static_cast<uint8_t>(TrackSubposition) - 1U) };
8725         }
8726     }
8727 
8728     ClearUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL);
8729     SetTrackType(tileElement->AsTrack()->GetTrackType());
8730     SetTrackDirection(direction);
8731     var_CF = tileElement->AsTrack()->GetBrakeBoosterSpeed();
8732     track_progress = 0;
8733 
8734 loc_6DC743:
8735     if (!IsHead())
8736     {
8737         animation_frame++;
8738         if (animation_frame >= 6)
8739         {
8740             animation_frame = 0;
8741         }
8742     }
8743     const rct_vehicle_info* moveInfo;
8744     for (;;)
8745     {
8746         moveInfo = GetMoveInfo();
8747         if (moveInfo->x != LOCATION_NULL)
8748         {
8749             break;
8750         }
8751         switch (MiniGolfState(moveInfo->y))
8752         {
8753             case MiniGolfState::Unk0: // loc_6DC7B4
8754                 if (!IsHead())
8755                 {
8756                     mini_golf_flags |= MiniGolfFlag::Flag3;
8757                 }
8758                 else
8759                 {
8760                     uint16_t rand16 = scenario_rand() & 0xFFFF;
8761                     VehicleTrackSubposition nextTrackSubposition = VehicleTrackSubposition::MiniGolfBallPathC14;
8762                     if (rand16 <= 0xA000)
8763                     {
8764                         nextTrackSubposition = VehicleTrackSubposition::MiniGolfBallPathB12;
8765                         if (rand16 <= 0x900)
8766                         {
8767                             nextTrackSubposition = VehicleTrackSubposition::MiniGolfBallPathA10;
8768                         }
8769                     }
8770                     TrackSubposition = nextTrackSubposition;
8771                 }
8772                 track_progress++;
8773                 break;
8774             case MiniGolfState::Unk1: // loc_6DC7ED
8775                 log_error("Unused move info...");
8776                 assert(false);
8777                 var_D3 = static_cast<uint8_t>(moveInfo->z);
8778                 track_progress++;
8779                 break;
8780             case MiniGolfState::Unk2: // loc_6DC800
8781                 mini_golf_flags |= MiniGolfFlag::Flag0;
8782                 track_progress++;
8783                 break;
8784             case MiniGolfState::Unk3: // loc_6DC810
8785                 mini_golf_flags |= MiniGolfFlag::Flag1;
8786                 track_progress++;
8787                 break;
8788             case MiniGolfState::Unk4: // loc_6DC820
8789             {
8790                 auto animation = MiniGolfAnimation(moveInfo->z);
8791                 // When the ride is closed occasionally the peep is removed
8792                 // but the vehicle is still on the track. This will prevent
8793                 // it from crashing in that situation.
8794                 auto* curPeep = TryGetEntity<Guest>(peep[0]);
8795                 if (curPeep != nullptr)
8796                 {
8797                     if (animation == MiniGolfAnimation::SwingLeft)
8798                     {
8799                         if (curPeep->Id & 7)
8800                         {
8801                             animation = MiniGolfAnimation::Swing;
8802                         }
8803                     }
8804                     if (animation == MiniGolfAnimation::PuttLeft)
8805                     {
8806                         if (curPeep->Id & 7)
8807                         {
8808                             animation = MiniGolfAnimation::Putt;
8809                         }
8810                     }
8811                 }
8812                 mini_golf_current_animation = animation;
8813                 animation_frame = 0;
8814                 track_progress++;
8815                 break;
8816             }
8817             case MiniGolfState::Unk5: // loc_6DC87A
8818                 mini_golf_flags |= MiniGolfFlag::Flag2;
8819                 track_progress++;
8820                 break;
8821             case MiniGolfState::Unk6: // loc_6DC88A
8822                 mini_golf_flags &= ~MiniGolfFlag::Flag4;
8823                 mini_golf_flags |= MiniGolfFlag::Flag5;
8824                 track_progress++;
8825                 break;
8826             default:
8827                 log_error("Invalid move info...");
8828                 assert(false);
8829                 break;
8830         }
8831     }
8832 
8833     // loc_6DC8A1
8834     trackPos = { TrackLocation.x + moveInfo->x, TrackLocation.y + moveInfo->y,
8835                  TrackLocation.z + moveInfo->z + GetRideTypeDescriptor(curRide->type).Heights.VehicleZOffset };
8836 
8837     remaining_distance -= 0x368A;
8838     if (remaining_distance < 0)
8839     {
8840         remaining_distance = 0;
8841     }
8842 
8843     unk_F64E20 = trackPos;
8844     sprite_direction = moveInfo->direction;
8845     bank_rotation = moveInfo->bank_rotation;
8846     Pitch = moveInfo->Pitch;
8847 
8848     if (rideEntry->vehicles[0].flags & VEHICLE_ENTRY_FLAG_WOODEN_WILD_MOUSE_SWING)
8849     {
8850         if (Pitch != 0)
8851         {
8852             SwingSprite = 0;
8853             SwingPosition = 0;
8854             SwingSpeed = 0;
8855         }
8856     }
8857 
8858     if (this == _vehicleFrontVehicle)
8859     {
8860         if (_vehicleVelocityF64E08 >= 0)
8861         {
8862             otherVehicleIndex = prev_vehicle_on_ride;
8863             UpdateMotionCollisionDetection(trackPos, &otherVehicleIndex);
8864         }
8865     }
8866     goto loc_6DC99A;
8867 
8868 loc_6DC985:
8869     remaining_distance -= 0x368A;
8870     if (remaining_distance < 0)
8871     {
8872         remaining_distance = 0;
8873     }
8874 
8875 loc_6DC99A:
8876     if (remaining_distance < 0x368A)
8877     {
8878         goto loc_6DCDE4;
8879     }
8880     acceleration = dword_9A2970[Pitch];
8881     _vehicleUnkF64E10++;
8882     goto loc_6DC462;
8883 
8884 loc_6DC9BC:
8885     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5;
8886     _vehicleVelocityF64E0C -= remaining_distance + 1;
8887     remaining_distance = -1;
8888     goto loc_6DCD2B;
8889 
8890 loc_6DCA9A:
8891     if (track_progress != 0)
8892     {
8893         track_progress -= 1;
8894         goto loc_6DCC2C;
8895     }
8896 
8897     tileElement = map_get_track_element_at_of_type_seq(TrackLocation, GetTrackType(), 0);
8898     {
8899         track_begin_end trackBeginEnd;
8900         if (!track_block_get_previous({ TrackLocation, tileElement }, &trackBeginEnd))
8901         {
8902             goto loc_6DC9BC;
8903         }
8904         trackPos = { trackBeginEnd.begin_x, trackBeginEnd.begin_y, trackBeginEnd.begin_z };
8905         direction = trackBeginEnd.begin_direction;
8906         tileElement = trackBeginEnd.begin_element;
8907     }
8908 
8909     if (PitchAndRollStart(HasUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES), tileElement)
8910         != TrackPitchAndRollEnd(GetTrackType()))
8911     {
8912         goto loc_6DCD4A;
8913     }
8914 
8915     {
8916         int32_t rideType = get_ride(tileElement->AsTrack()->GetRideIndex())->type;
8917         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES);
8918         if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE))
8919         {
8920             if (tileElement->AsTrack()->IsInverted())
8921             {
8922                 SetUpdateFlag(VEHICLE_UPDATE_FLAG_USE_INVERTED_SPRITES);
8923             }
8924         }
8925     }
8926 
8927     TrackLocation = trackPos;
8928 
8929     if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL))
8930     {
8931         ClearUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL);
8932         if (next_vehicle_on_train == SPRITE_INDEX_NULL)
8933         {
8934             if (_vehicleVelocityF64E08 < 0)
8935             {
8936                 _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_8;
8937             }
8938         }
8939     }
8940 
8941     SetTrackType(tileElement->AsTrack()->GetTrackType());
8942     SetTrackDirection(direction);
8943     var_CF = tileElement->AsTrack()->GetSeatRotation() << 1;
8944 
8945     // There are two bytes before the move info list
8946     track_progress = GetTrackProgress();
8947 
8948 loc_6DCC2C:
8949     moveInfo = GetMoveInfo();
8950     trackPos = { TrackLocation.x + moveInfo->x, TrackLocation.y + moveInfo->y,
8951                  TrackLocation.z + moveInfo->z + GetRideTypeDescriptor(curRide->type).Heights.VehicleZOffset };
8952 
8953     remaining_distance -= 0x368A;
8954     if (remaining_distance < 0)
8955     {
8956         remaining_distance = 0;
8957     }
8958 
8959     unk_F64E20 = trackPos;
8960     sprite_direction = moveInfo->direction;
8961     bank_rotation = moveInfo->bank_rotation;
8962     Pitch = moveInfo->Pitch;
8963 
8964     if (rideEntry->vehicles[0].flags & VEHICLE_ENTRY_FLAG_WOODEN_WILD_MOUSE_SWING)
8965     {
8966         if (Pitch != 0)
8967         {
8968             SwingSprite = 0;
8969             SwingPosition = 0;
8970             SwingSpeed = 0;
8971         }
8972     }
8973 
8974     if (this == _vehicleFrontVehicle)
8975     {
8976         if (_vehicleVelocityF64E08 >= 0)
8977         {
8978             otherVehicleIndex = var_44;
8979             if (UpdateMotionCollisionDetection(trackPos, &otherVehicleIndex))
8980             {
8981                 goto loc_6DCD6B;
8982             }
8983         }
8984     }
8985 
8986 loc_6DCD2B:
8987     if (remaining_distance >= 0)
8988     {
8989         goto loc_6DCDE4;
8990     }
8991     acceleration += dword_9A2970[Pitch];
8992     _vehicleUnkF64E10++;
8993     goto loc_6DCA9A;
8994 
8995 loc_6DCD4A:
8996     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5;
8997     _vehicleVelocityF64E0C -= remaining_distance - 0x368A;
8998     remaining_distance = 0x368A;
8999     goto loc_6DC99A;
9000 
9001 loc_6DCD6B:
9002     _vehicleVelocityF64E0C -= remaining_distance - 0x368A;
9003     remaining_distance = 0x368A;
9004     {
9005         Vehicle* vEBP = GetEntity<Vehicle>(otherVehicleIndex);
9006         if (vEBP == nullptr)
9007         {
9008             return;
9009         }
9010         Vehicle* vEDI = gCurrentVehicle;
9011         if (abs(vEDI->velocity - vEBP->velocity) > 0xE0000)
9012         {
9013             if (!(vehicleEntry->flags & VEHICLE_ENTRY_FLAG_BOAT_HIRE_COLLISION_DETECTION))
9014             {
9015                 _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_COLLISION;
9016             }
9017         }
9018         vEDI->velocity = vEBP->velocity >> 1;
9019         vEBP->velocity = vEDI->velocity >> 1;
9020     }
9021     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_2;
9022     goto loc_6DC99A;
9023 
9024 loc_6DCDE4:
9025     MoveTo(unk_F64E20);
9026 
9027 loc_6DCE02:
9028     acceleration /= _vehicleUnkF64E10;
9029     if (TrackSubposition == VehicleTrackSubposition::ChairliftGoingBack)
9030     {
9031         return;
9032     }
9033     {
9034         auto trackType = GetTrackType();
9035         const auto& ted = GetTrackElementDescriptor(trackType);
9036         if (!(ted.SequenceProperties[0] & TRACK_SEQUENCE_FLAG_ORIGIN))
9037         {
9038             return;
9039         }
9040         _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_3;
9041         if (trackType != TrackElemType::EndStation)
9042         {
9043             return;
9044         }
9045     }
9046     if (this != gCurrentVehicle)
9047     {
9048         return;
9049     }
9050     if (_vehicleVelocityF64E08 < 0)
9051     {
9052         if (track_progress > 11)
9053         {
9054             return;
9055         }
9056     }
9057     if (track_progress <= 8)
9058     {
9059         return;
9060     }
9061 
9062     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION;
9063 
9064     for (int32_t i = 0; i < MAX_STATIONS; i++)
9065     {
9066         if (TrackLocation != curRide->stations[i].Start)
9067         {
9068             continue;
9069         }
9070         if (TrackLocation.z != curRide->stations[i].GetBaseZ())
9071         {
9072             continue;
9073         }
9074         _vehicleStationIndex = i;
9075     }
9076 }
9077 
GetAccelerationDecrease2(const int32_t velocity,const int32_t totalMass)9078 static constexpr int32_t GetAccelerationDecrease2(const int32_t velocity, const int32_t totalMass)
9079 {
9080     int32_t accelerationDecrease2 = velocity >> 8;
9081     accelerationDecrease2 *= accelerationDecrease2;
9082     if (velocity < 0)
9083     {
9084         accelerationDecrease2 = -accelerationDecrease2;
9085     }
9086     accelerationDecrease2 >>= 4;
9087     // OpenRCT2: vehicles from different track types can have  0 mass.
9088     if (totalMass != 0)
9089     {
9090         return accelerationDecrease2 / totalMass;
9091     }
9092 
9093     return accelerationDecrease2;
9094 }
9095 
UpdateTrackMotionMiniGolfCalculateAcceleration(const rct_ride_entry_vehicle & vehicleEntry)9096 int32_t Vehicle::UpdateTrackMotionMiniGolfCalculateAcceleration(const rct_ride_entry_vehicle& vehicleEntry)
9097 {
9098     int32_t sumAcceleration = 0;
9099     int32_t numVehicles = 0;
9100     uint16_t totalMass = 0;
9101 
9102     for (Vehicle* vehicle = this; vehicle != nullptr; vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
9103     {
9104         numVehicles++;
9105         totalMass += vehicle->mass;
9106         sumAcceleration += vehicle->acceleration;
9107     }
9108 
9109     int32_t newAcceleration = ((sumAcceleration / numVehicles) * 21) >> 9;
9110     newAcceleration -= velocity >> 12;
9111     newAcceleration -= GetAccelerationDecrease2(velocity, totalMass);
9112 
9113     if (!(vehicleEntry.flags & VEHICLE_ENTRY_FLAG_POWERED))
9114     {
9115         return newAcceleration;
9116     }
9117     if (vehicleEntry.flags & VEHICLE_ENTRY_FLAG_POWERED_RIDE_UNRESTRICTED_GRAVITY)
9118     {
9119         if (speed * 0x4000 < velocity)
9120         {
9121             return newAcceleration;
9122         }
9123     }
9124     {
9125         int32_t poweredAcceleration = speed << 14;
9126         int32_t quarterForce = (speed * totalMass) >> 2;
9127         if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_REVERSING_SHUTTLE))
9128         {
9129             poweredAcceleration = -poweredAcceleration;
9130         }
9131         poweredAcceleration -= velocity;
9132         poweredAcceleration *= powered_acceleration << 1;
9133         if (quarterForce != 0)
9134             poweredAcceleration /= quarterForce;
9135 
9136         if (vehicleEntry.flags & VEHICLE_ENTRY_FLAG_WATER_RIDE)
9137         {
9138             if (poweredAcceleration < 0)
9139             {
9140                 poweredAcceleration >>= 4;
9141             }
9142 
9143             if (vehicleEntry.flags & VEHICLE_ENTRY_FLAG_SPINNING)
9144             {
9145                 spin_speed = std::clamp(spin_speed, VEHICLE_MIN_SPIN_SPEED_WATER_RIDE, VEHICLE_MAX_SPIN_SPEED_WATER_RIDE);
9146             }
9147 
9148             if (Pitch != 0)
9149             {
9150                 poweredAcceleration = std::max(0, poweredAcceleration);
9151                 if (vehicleEntry.flags & VEHICLE_ENTRY_FLAG_SPINNING)
9152                 {
9153                     if (Pitch == 2)
9154                     {
9155                         spin_speed = 0;
9156                     }
9157                 }
9158                 newAcceleration += poweredAcceleration;
9159                 return newAcceleration;
9160             }
9161         }
9162 
9163         if (abs(velocity) > 0x10000)
9164         {
9165             newAcceleration = 0;
9166         }
9167         newAcceleration += poweredAcceleration;
9168     }
9169 
9170     return newAcceleration;
9171 }
9172 
UpdateTrackMotionMiniGolf(int32_t * outStation)9173 int32_t Vehicle::UpdateTrackMotionMiniGolf(int32_t* outStation)
9174 {
9175     auto curRide = GetRide();
9176     if (curRide == nullptr)
9177         return 0;
9178 
9179     rct_ride_entry* rideEntry = GetRideEntry();
9180     rct_ride_entry_vehicle* vehicleEntry = Entry();
9181 
9182     gCurrentVehicle = this;
9183     _vehicleMotionTrackFlags = 0;
9184     velocity += acceleration;
9185     _vehicleVelocityF64E08 = velocity;
9186     _vehicleVelocityF64E0C = (velocity >> 10) * 42;
9187     _vehicleFrontVehicle = _vehicleVelocityF64E08 < 0 ? TrainTail() : this;
9188 
9189     for (Vehicle* vehicle = _vehicleFrontVehicle; vehicle != nullptr;)
9190     {
9191         vehicle->UpdateTrackMotionMiniGolfVehicle(curRide, rideEntry, vehicleEntry);
9192         if (vehicle->HasUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL))
9193         {
9194             _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_ON_LIFT_HILL;
9195         }
9196         if (vehicle->HasUpdateFlag(VEHICLE_UPDATE_FLAG_SINGLE_CAR_POSITION))
9197         {
9198             if (outStation != nullptr)
9199                 *outStation = _vehicleStationIndex;
9200             return _vehicleMotionTrackFlags;
9201         }
9202         if (_vehicleVelocityF64E08 >= 0)
9203         {
9204             vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train);
9205         }
9206         else
9207         {
9208             if (vehicle == gCurrentVehicle)
9209             {
9210                 break;
9211             }
9212             vehicle = GetEntity<Vehicle>(vehicle->prev_vehicle_on_ride);
9213         }
9214     }
9215 
9216     acceleration = UpdateTrackMotionMiniGolfCalculateAcceleration(*vehicleEntry);
9217 
9218     if (outStation != nullptr)
9219         *outStation = _vehicleStationIndex;
9220     return _vehicleMotionTrackFlags;
9221 }
9222 
9223 /**
9224  *
9225  *  rct2: 0x006DC1E4
9226  */
modified_speed(uint16_t trackType,VehicleTrackSubposition trackSubposition,uint8_t speed)9227 static uint8_t modified_speed(uint16_t trackType, VehicleTrackSubposition trackSubposition, uint8_t speed)
9228 {
9229     enum
9230     {
9231         FULL_SPEED,
9232         THREE_QUARTER_SPEED,
9233         HALF_SPEED
9234     };
9235 
9236     uint8_t speedModifier = FULL_SPEED;
9237 
9238     if (trackType == TrackElemType::LeftQuarterTurn1Tile)
9239     {
9240         speedModifier = (trackSubposition == VehicleTrackSubposition::GoKartsLeftLane) ? HALF_SPEED : THREE_QUARTER_SPEED;
9241     }
9242     else if (trackType == TrackElemType::RightQuarterTurn1Tile)
9243     {
9244         speedModifier = (trackSubposition == VehicleTrackSubposition::GoKartsRightLane) ? HALF_SPEED : THREE_QUARTER_SPEED;
9245     }
9246 
9247     switch (speedModifier)
9248     {
9249         case HALF_SPEED:
9250             return speed >> 1;
9251         case THREE_QUARTER_SPEED:
9252             return speed - (speed >> 2);
9253     }
9254     return speed;
9255 }
9256 
UpdateTrackMotionPoweredRideAcceleration(rct_ride_entry_vehicle * vehicleEntry,uint32_t totalMass,const int32_t curAcceleration)9257 int32_t Vehicle::UpdateTrackMotionPoweredRideAcceleration(
9258     rct_ride_entry_vehicle* vehicleEntry, uint32_t totalMass, const int32_t curAcceleration)
9259 {
9260     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_POWERED_RIDE_UNRESTRICTED_GRAVITY)
9261     {
9262         if (velocity > (speed * 0x4000))
9263         {
9264             // Same code as none powered rides
9265             if (curAcceleration <= 0 && curAcceleration >= -500 && velocity <= 0x8000)
9266             {
9267                 return curAcceleration + 400;
9268             }
9269             return curAcceleration;
9270         }
9271     }
9272     uint8_t modifiedSpeed = modified_speed(GetTrackType(), TrackSubposition, speed);
9273     int32_t poweredAcceleration = modifiedSpeed << 14;
9274     int32_t quarterForce = (modifiedSpeed * totalMass) >> 2;
9275     if (HasUpdateFlag(VEHICLE_UPDATE_FLAG_REVERSING_SHUTTLE))
9276     {
9277         poweredAcceleration = -poweredAcceleration;
9278     }
9279     poweredAcceleration -= velocity;
9280     poweredAcceleration *= powered_acceleration << 1;
9281     if (quarterForce != 0)
9282     {
9283         poweredAcceleration /= quarterForce;
9284     }
9285 
9286     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_LIFT)
9287     {
9288         poweredAcceleration *= 4;
9289     }
9290 
9291     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_WATER_RIDE)
9292     {
9293         if (poweredAcceleration < 0)
9294         {
9295             poweredAcceleration >>= 4;
9296         }
9297 
9298         if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_SPINNING)
9299         {
9300             spin_speed = std::clamp(spin_speed, VEHICLE_MIN_SPIN_SPEED_WATER_RIDE, VEHICLE_MAX_SPIN_SPEED_WATER_RIDE);
9301         }
9302 
9303         if (Pitch != 0)
9304         {
9305             if (poweredAcceleration < 0)
9306             {
9307                 poweredAcceleration = 0;
9308             }
9309 
9310             if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_SPINNING)
9311             {
9312                 // If the vehicle is on the up slope kill the spin speedModifier
9313                 if (Pitch == 2)
9314                 {
9315                     spin_speed = 0;
9316                 }
9317             }
9318             return curAcceleration + poweredAcceleration;
9319         }
9320     }
9321 
9322     if (std::abs(velocity) <= 0x10000)
9323     {
9324         return poweredAcceleration;
9325     }
9326 
9327     return curAcceleration + poweredAcceleration;
9328 }
9329 
9330 /**
9331  *
9332  *  rct2: 0x006DAB4C
9333  */
UpdateTrackMotion(int32_t * outStation)9334 int32_t Vehicle::UpdateTrackMotion(int32_t* outStation)
9335 {
9336     auto curRide = GetRide();
9337     if (curRide == nullptr)
9338         return 0;
9339 
9340     rct_ride_entry* rideEntry = GetRideEntry();
9341     auto vehicleEntry = Entry();
9342 
9343     if (vehicleEntry == nullptr)
9344     {
9345         return 0;
9346     }
9347 
9348     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_MINI_GOLF)
9349     {
9350         return UpdateTrackMotionMiniGolf(outStation);
9351     }
9352 
9353     _vehicleF64E2C = 0;
9354     gCurrentVehicle = this;
9355     _vehicleMotionTrackFlags = 0;
9356     _vehicleStationIndex = STATION_INDEX_NULL;
9357 
9358     UpdateTrackMotionUpStopCheck();
9359     CheckAndApplyBlockSectionStopSite();
9360     UpdateVelocity();
9361 
9362     Vehicle* vehicle = this;
9363     if (_vehicleVelocityF64E08 < 0)
9364     {
9365         vehicle = vehicle->TrainTail();
9366     }
9367     // This will be the front vehicle even when traveling
9368     // backwards.
9369     _vehicleFrontVehicle = vehicle;
9370 
9371     uint16_t spriteId = vehicle->sprite_index;
9372     while (spriteId != SPRITE_INDEX_NULL)
9373     {
9374         Vehicle* car = GetEntity<Vehicle>(spriteId);
9375         if (car == nullptr)
9376         {
9377             break;
9378         }
9379         vehicleEntry = car->Entry();
9380         if (vehicleEntry == nullptr)
9381         {
9382             goto loc_6DBF3E;
9383         }
9384 
9385         // Swinging cars
9386         if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_SWINGING)
9387         {
9388             car->UpdateSwingingCar();
9389         }
9390         // Spinning cars
9391         if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_SPINNING)
9392         {
9393             car->UpdateSpinningCar();
9394         }
9395         // Rider sprites?? animation??
9396         if ((vehicleEntry->flags & VEHICLE_ENTRY_FLAG_VEHICLE_ANIMATION)
9397             || (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_RIDER_ANIMATION))
9398         {
9399             car->UpdateAdditionalAnimation();
9400         }
9401         car->acceleration = dword_9A2970[car->Pitch];
9402         _vehicleUnkF64E10 = 1;
9403 
9404         if (!car->HasUpdateFlag(VEHICLE_UPDATE_FLAG_SINGLE_CAR_POSITION))
9405         {
9406             car->remaining_distance += _vehicleVelocityF64E0C;
9407         }
9408 
9409         car->sound2_flags &= ~VEHICLE_SOUND2_FLAGS_LIFT_HILL;
9410         unk_F64E20.x = car->x;
9411         unk_F64E20.y = car->y;
9412         unk_F64E20.z = car->z;
9413         car->Invalidate();
9414 
9415         while (true)
9416         {
9417             if (car->remaining_distance < 0)
9418             {
9419                 // Backward loop
9420                 if (car->UpdateTrackMotionBackwards(vehicleEntry, curRide, rideEntry))
9421                 {
9422                     break;
9423                 }
9424 
9425                 if (car->remaining_distance < 0x368A)
9426                 {
9427                     break;
9428                 }
9429                 car->acceleration += dword_9A2970[car->Pitch];
9430                 _vehicleUnkF64E10++;
9431                 continue;
9432             }
9433             if (car->remaining_distance < 0x368A)
9434             {
9435                 // Location found
9436                 goto loc_6DBF3E;
9437             }
9438             if (car->UpdateTrackMotionForwards(vehicleEntry, curRide, rideEntry))
9439             {
9440                 break;
9441             }
9442 
9443             if (car->remaining_distance >= 0)
9444             {
9445                 break;
9446             }
9447             car->acceleration = dword_9A2970[car->Pitch];
9448             _vehicleUnkF64E10++;
9449             continue;
9450         }
9451         // loc_6DBF20
9452         car->MoveTo(unk_F64E20);
9453 
9454     loc_6DBF3E:
9455         car->Sub6DBF3E();
9456 
9457         // loc_6DC0F7
9458         if (car->HasUpdateFlag(VEHICLE_UPDATE_FLAG_ON_LIFT_HILL))
9459         {
9460             _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_ON_LIFT_HILL;
9461         }
9462         if (car->HasUpdateFlag(VEHICLE_UPDATE_FLAG_SINGLE_CAR_POSITION))
9463         {
9464             if (outStation != nullptr)
9465                 *outStation = _vehicleStationIndex;
9466             return _vehicleMotionTrackFlags;
9467         }
9468         if (_vehicleVelocityF64E08 >= 0)
9469         {
9470             spriteId = car->next_vehicle_on_train;
9471         }
9472         else
9473         {
9474             if (car == gCurrentVehicle)
9475             {
9476                 break;
9477             }
9478             spriteId = car->prev_vehicle_on_ride;
9479         }
9480     }
9481     // loc_6DC144
9482     vehicle = gCurrentVehicle;
9483 
9484     vehicleEntry = vehicle->Entry();
9485     // eax
9486     int32_t totalAcceleration = 0;
9487     // ebp
9488     int32_t totalMass = 0;
9489     // ebx
9490     int32_t numVehicles = 0;
9491 
9492     for (; vehicle != nullptr; vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
9493     {
9494         numVehicles++;
9495         totalMass += vehicle->mass;
9496         totalAcceleration += vehicle->acceleration;
9497     }
9498 
9499     vehicle = gCurrentVehicle;
9500     int32_t newAcceleration = (totalAcceleration / numVehicles) * 21;
9501     if (newAcceleration < 0)
9502     {
9503         newAcceleration += 511;
9504     }
9505     newAcceleration >>= 9;
9506 
9507     int32_t curAcceleration = newAcceleration;
9508     curAcceleration -= vehicle->velocity / 4096;
9509     curAcceleration -= GetAccelerationDecrease2(vehicle->velocity, totalMass);
9510 
9511     if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_POWERED)
9512     {
9513         curAcceleration = vehicle->UpdateTrackMotionPoweredRideAcceleration(vehicleEntry, totalMass, curAcceleration);
9514     }
9515     else if (curAcceleration <= 0 && curAcceleration >= -500)
9516     {
9517         // Probably moving slowly on a flat track piece, low rolling resistance and drag.
9518 
9519         if (vehicle->velocity <= 0x8000 && vehicle->velocity >= 0)
9520         {
9521             // Vehicle is creeping forwards very slowly (less than ~2km/h), boost speed a bit.
9522             curAcceleration += 400;
9523         }
9524     }
9525 
9526     if (vehicle->GetTrackType() == TrackElemType::Watersplash)
9527     {
9528         if (vehicle->track_progress >= 48 && vehicle->track_progress <= 128)
9529         {
9530             curAcceleration -= vehicle->velocity >> 6;
9531         }
9532     }
9533 
9534     if (rideEntry->flags & RIDE_ENTRY_FLAG_PLAY_SPLASH_SOUND_SLIDE)
9535     {
9536         if (vehicle->IsHead())
9537         {
9538             if (track_element_is_covered(vehicle->GetTrackType()))
9539             {
9540                 if (vehicle->velocity > 0x20000)
9541                 {
9542                     curAcceleration -= vehicle->velocity >> 6;
9543                 }
9544             }
9545         }
9546     }
9547 
9548     vehicle->acceleration = curAcceleration;
9549 
9550     // hook_setreturnregisters(&regs);
9551     if (outStation != nullptr)
9552         *outStation = _vehicleStationIndex;
9553     return _vehicleMotionTrackFlags;
9554 }
9555 
GetRideEntry() const9556 rct_ride_entry* Vehicle::GetRideEntry() const
9557 {
9558     return get_ride_entry(ride_subtype);
9559 }
9560 
Entry() const9561 rct_ride_entry_vehicle* Vehicle::Entry() const
9562 {
9563     rct_ride_entry* rideEntry = GetRideEntry();
9564     if (rideEntry == nullptr)
9565     {
9566         return nullptr;
9567     }
9568     return &rideEntry->vehicles[vehicle_type];
9569 }
9570 
GetRide() const9571 Ride* Vehicle::GetRide() const
9572 {
9573     return get_ride(ride);
9574 }
9575 
NumPeepsUntilTrainTail() const9576 int32_t Vehicle::NumPeepsUntilTrainTail() const
9577 {
9578     int32_t numPeeps = 0;
9579     for (const Vehicle* vehicle = GetEntity<Vehicle>(sprite_index); vehicle != nullptr;
9580          vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
9581     {
9582         numPeeps += vehicle->num_peeps;
9583     }
9584 
9585     return numPeeps;
9586 }
9587 
9588 /**
9589  *
9590  *  rct2: 0x006DA1EC
9591  */
InvalidateWindow()9592 void Vehicle::InvalidateWindow()
9593 {
9594     auto intent = Intent(INTENT_ACTION_INVALIDATE_VEHICLE_WINDOW);
9595     intent.putExtra(INTENT_EXTRA_VEHICLE, this);
9596     context_broadcast_intent(&intent);
9597 }
9598 
UpdateCrossings() const9599 void Vehicle::UpdateCrossings() const
9600 {
9601     if (TrainHead() != this)
9602     {
9603         return;
9604     }
9605 
9606     const Vehicle* frontVehicle{};
9607     const Vehicle* backVehicle{};
9608 
9609     bool travellingForwards = !HasUpdateFlag(VEHICLE_UPDATE_FLAG_REVERSING_SHUTTLE);
9610 
9611     if (travellingForwards)
9612     {
9613         frontVehicle = this;
9614         backVehicle = TrainTail();
9615     }
9616     else
9617     {
9618         frontVehicle = TrainTail();
9619         backVehicle = this;
9620     }
9621 
9622     track_begin_end output{};
9623     int32_t direction{};
9624 
9625     CoordsXYE xyElement = { frontVehicle->TrackLocation,
9626                             map_get_track_element_at_of_type_seq(
9627                                 frontVehicle->TrackLocation, frontVehicle->GetTrackType(), 0) };
9628     int32_t curZ = frontVehicle->TrackLocation.z;
9629 
9630     if (xyElement.element != nullptr && status != Vehicle::Status::Arriving)
9631     {
9632         int16_t autoReserveAhead = 4 + abs(velocity) / 150000;
9633         int16_t crossingBonus = 0;
9634         bool playedClaxon = false;
9635 
9636         // vehicle positions mean we have to take larger
9637         //  margins for travelling backwards
9638         if (!travellingForwards)
9639         {
9640             autoReserveAhead += 1;
9641         }
9642 
9643         while (true)
9644         {
9645             auto* pathElement = map_get_path_element_at(TileCoordsXYZ(CoordsXYZ{ xyElement, xyElement.element->GetBaseZ() }));
9646             auto curRide = GetRide();
9647 
9648             // Many New Element parks have invisible rides hacked into the path.
9649             // Limit path blocking to rides actually supporting level crossings to prevent peeps getting stuck everywhere.
9650             if (pathElement != nullptr && curRide != nullptr
9651                 && GetRideTypeDescriptor(curRide->type).HasFlag(RIDE_TYPE_FLAG_SUPPORTS_LEVEL_CROSSINGS))
9652             {
9653                 if (!playedClaxon && !pathElement->IsBlockedByVehicle())
9654                 {
9655                     Claxon();
9656                 }
9657                 crossingBonus = 4;
9658                 pathElement->SetIsBlockedByVehicle(true);
9659             }
9660             else
9661             {
9662                 crossingBonus = 0;
9663             }
9664 
9665             if (--autoReserveAhead + crossingBonus <= 0)
9666             {
9667                 break;
9668             }
9669 
9670             curZ = xyElement.element->base_height;
9671 
9672             if (travellingForwards)
9673             {
9674                 if (!track_block_get_next(&xyElement, &xyElement, &curZ, &direction))
9675                 {
9676                     break;
9677                 }
9678             }
9679             else
9680             {
9681                 if (!track_block_get_previous(xyElement, &output))
9682                 {
9683                     break;
9684                 }
9685                 xyElement.x = output.begin_x;
9686                 xyElement.y = output.begin_y;
9687                 xyElement.element = output.begin_element;
9688             }
9689 
9690             if (xyElement.element->AsTrack()->IsStation())
9691             {
9692                 break;
9693             }
9694         }
9695     }
9696 
9697     xyElement = { backVehicle->TrackLocation,
9698                   map_get_track_element_at_of_type_seq(backVehicle->TrackLocation, backVehicle->GetTrackType(), 0) };
9699     curZ = backVehicle->TrackLocation.z;
9700 
9701     if (xyElement.element != nullptr)
9702     {
9703         uint8_t freeCount = travellingForwards ? 3 : 1;
9704 
9705         while (freeCount-- > 0)
9706         {
9707             if (travellingForwards)
9708             {
9709                 if (track_block_get_previous(xyElement, &output))
9710                 {
9711                     xyElement.x = output.begin_x;
9712                     xyElement.y = output.begin_y;
9713                     xyElement.element = output.begin_element;
9714                 }
9715             }
9716 
9717             auto* pathElement = map_get_path_element_at(TileCoordsXYZ(CoordsXYZ{ xyElement, xyElement.element->GetBaseZ() }));
9718             if (pathElement != nullptr)
9719             {
9720                 pathElement->SetIsBlockedByVehicle(false);
9721             }
9722         }
9723     }
9724 }
9725 
Claxon() const9726 void Vehicle::Claxon() const
9727 {
9728     rct_ride_entry* rideEntry = GetRideEntry();
9729     switch (rideEntry->vehicles[vehicle_type].sound_range)
9730     {
9731         case SOUND_RANGE_WHISTLE:
9732             OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::TrainWhistle, { x, y, z });
9733             break;
9734         case SOUND_RANGE_BELL:
9735             OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Tram, { x, y, z });
9736             break;
9737     }
9738 }
9739 
GetHead()9740 Vehicle* Vehicle::GetHead()
9741 {
9742     auto v = this;
9743     while (v != nullptr && !v->IsHead())
9744     {
9745         v = GetEntity<Vehicle>(v->prev_vehicle_on_ride);
9746     }
9747     return v;
9748 }
9749 
GetHead() const9750 const Vehicle* Vehicle::GetHead() const
9751 {
9752     return (const_cast<Vehicle*>(this)->GetHead());
9753 }
9754 
GetCar(size_t carIndex) const9755 Vehicle* Vehicle::GetCar(size_t carIndex) const
9756 {
9757     auto car = const_cast<Vehicle*>(this);
9758     for (; carIndex != 0; carIndex--)
9759     {
9760         car = GetEntity<Vehicle>(car->next_vehicle_on_train);
9761         if (car == nullptr)
9762         {
9763             log_error("Tried to get non-existent car from index!");
9764             return nullptr;
9765         }
9766     }
9767     return car;
9768 }
9769 
SetState(Vehicle::Status vehicleStatus,uint8_t subState)9770 void Vehicle::SetState(Vehicle::Status vehicleStatus, uint8_t subState)
9771 {
9772     status = vehicleStatus;
9773     sub_state = subState;
9774     InvalidateWindow();
9775 }
9776 
IsGhost() const9777 bool Vehicle::IsGhost() const
9778 {
9779     auto r = GetRide();
9780     return r != nullptr && r->status == RideStatus::Simulating;
9781 }
9782 
EnableCollisionsForTrain()9783 void Vehicle::EnableCollisionsForTrain()
9784 {
9785     assert(this->IsHead());
9786     for (auto vehicle = this; vehicle != nullptr; vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
9787     {
9788         vehicle->ClearUpdateFlag(VEHICLE_UPDATE_FLAG_COLLISION_DISABLED);
9789     }
9790 }
9791