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 "CableLift.h"
11 
12 #include "../audio/audio.h"
13 #include "../rct12/RCT12.h"
14 #include "../util/Util.h"
15 #include "../world/EntityList.h"
16 #include "Ride.h"
17 #include "RideData.h"
18 #include "Track.h"
19 #include "Vehicle.h"
20 #include "VehicleData.h"
21 
22 #include <algorithm>
23 
cable_lift_segment_create(Ride & ride,int32_t x,int32_t y,int32_t z,int32_t direction,uint16_t var_44,int32_t remaining_distance,bool head)24 Vehicle* cable_lift_segment_create(
25     Ride& ride, int32_t x, int32_t y, int32_t z, int32_t direction, uint16_t var_44, int32_t remaining_distance, bool head)
26 {
27     Vehicle* current = CreateEntity<Vehicle>();
28     current->ride = ride.id;
29     current->ride_subtype = OBJECT_ENTRY_INDEX_NULL;
30     if (head)
31     {
32         ride.cable_lift = current->sprite_index;
33     }
34     current->SubType = head ? Vehicle::Type::Head : Vehicle::Type::Tail;
35     current->var_44 = var_44;
36     current->remaining_distance = remaining_distance;
37     current->sprite_width = 10;
38     current->sprite_height_negative = 10;
39     current->sprite_height_positive = 10;
40     current->mass = 100;
41     current->num_seats = 0;
42     current->speed = 20;
43     current->powered_acceleration = 80;
44     current->velocity = 0;
45     current->acceleration = 0;
46     current->SwingSprite = 0;
47     current->SwingPosition = 0;
48     current->SwingSpeed = 0;
49     current->restraints_position = 0;
50     current->spin_sprite = 0;
51     current->spin_speed = 0;
52     current->sound2_flags = 0;
53     current->sound1_id = OpenRCT2::Audio::SoundId::Null;
54     current->sound2_id = OpenRCT2::Audio::SoundId::Null;
55     current->var_C4 = 0;
56     current->animation_frame = 0;
57     current->animationState = 0;
58     current->scream_sound_id = OpenRCT2::Audio::SoundId::Null;
59     current->Pitch = 0;
60     current->bank_rotation = 0;
61     for (auto& peep : current->peep)
62     {
63         peep = SPRITE_INDEX_NULL;
64     }
65     current->TrackSubposition = VehicleTrackSubposition::Default;
66     current->sprite_direction = direction << 3;
67 
68     z = z * COORDS_Z_STEP;
69     current->TrackLocation = { x, y, z };
70     z += ride.GetRideTypeDescriptor().Heights.VehicleZOffset;
71 
72     current->MoveTo({ 16, 16, z });
73     current->SetTrackType(TrackElemType::CableLiftHill);
74     current->SetTrackDirection(current->sprite_direction >> 3);
75     current->track_progress = 164;
76     current->update_flags = VEHICLE_UPDATE_FLAG_COLLISION_DISABLED;
77     current->SetState(Vehicle::Status::MovingToEndOfStation, 0);
78     current->num_peeps = 0;
79     current->next_free_seat = 0;
80     current->BoatLocation.SetNull();
81     current->IsCrashedVehicle = false;
82     return current;
83 }
84 
CableLiftUpdate()85 void Vehicle::CableLiftUpdate()
86 {
87     switch (status)
88     {
89         case Vehicle::Status::MovingToEndOfStation:
90             CableLiftUpdateMovingToEndOfStation();
91             break;
92         case Vehicle::Status::WaitingForPassengers:
93             // Stays in this state until a train puts it into next state
94             break;
95         case Vehicle::Status::WaitingToDepart:
96             CableLiftUpdateWaitingToDepart();
97             break;
98         case Vehicle::Status::Departing:
99             CableLiftUpdateDeparting();
100             break;
101         case Vehicle::Status::Travelling:
102             CableLiftUpdateTravelling();
103             break;
104         case Vehicle::Status::Arriving:
105             CableLiftUpdateArriving();
106             break;
107         default:
108             break;
109     }
110 }
111 
112 /**
113  *
114  *  rct2: 0x006DF8A4
115  */
CableLiftUpdateMovingToEndOfStation()116 void Vehicle::CableLiftUpdateMovingToEndOfStation()
117 {
118     if (velocity >= -439800)
119         acceleration = -2932;
120 
121     if (velocity < -439800)
122     {
123         velocity -= velocity / 16;
124         acceleration = 0;
125     }
126 
127     if (!(CableLiftUpdateTrackMotion() & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION))
128         return;
129 
130     velocity = 0;
131     acceleration = 0;
132     SetState(Vehicle::Status::WaitingForPassengers, sub_state);
133 }
134 
135 /**
136  *
137  *  rct2: 0x006DF8F1
138  */
CableLiftUpdateWaitingToDepart()139 void Vehicle::CableLiftUpdateWaitingToDepart()
140 {
141     if (velocity >= -58640)
142         acceleration = -14660;
143 
144     if (velocity < -58640)
145     {
146         velocity -= velocity / 16;
147         acceleration = 0;
148     }
149 
150     CableLiftUpdateTrackMotion();
151 
152     // Next check to see if the second part of the cable lift
153     // is at the front of the passenger vehicle to simulate the
154     // cable being attached underneath the train.
155     Vehicle* passengerVehicle = GetEntity<Vehicle>(cable_lift_target);
156     Vehicle* cableLiftSecondPart = GetEntity<Vehicle>(prev_vehicle_on_ride);
157     if (passengerVehicle == nullptr || cableLiftSecondPart == nullptr)
158     {
159         return;
160     }
161 
162     int16_t distX = abs(passengerVehicle->x - cableLiftSecondPart->x);
163     int16_t distY = abs(passengerVehicle->y - cableLiftSecondPart->y);
164 
165     if (distX + distY > 2)
166         return;
167 
168     velocity = 0;
169     acceleration = 0;
170     SetState(Vehicle::Status::Departing, 0);
171 }
172 
173 /**
174  *
175  *  rct2: 0x006DF97A
176  */
CableLiftUpdateDeparting()177 void Vehicle::CableLiftUpdateDeparting()
178 {
179     sub_state++;
180     if (sub_state < 16)
181         return;
182 
183     Vehicle* passengerVehicle = GetEntity<Vehicle>(cable_lift_target);
184     if (passengerVehicle == nullptr)
185     {
186         return;
187     }
188     SetState(Vehicle::Status::Travelling, sub_state);
189     passengerVehicle->SetState(Vehicle::Status::TravellingCableLift, passengerVehicle->sub_state);
190 }
191 
192 /**
193  *
194  *  rct2: 0x006DF99C
195  */
CableLiftUpdateTravelling()196 void Vehicle::CableLiftUpdateTravelling()
197 {
198     Vehicle* passengerVehicle = GetEntity<Vehicle>(cable_lift_target);
199     if (passengerVehicle == nullptr)
200     {
201         return;
202     }
203 
204     velocity = std::min(passengerVehicle->velocity, 439800);
205     acceleration = 0;
206     if (passengerVehicle->HasUpdateFlag(VEHICLE_UPDATE_FLAG_BROKEN_TRAIN))
207         return;
208 
209     if (!(CableLiftUpdateTrackMotion() & VEHICLE_UPDATE_MOTION_TRACK_FLAG_1))
210         return;
211 
212     velocity = 0;
213     acceleration = 0;
214     SetState(Vehicle::Status::Arriving, 0);
215 }
216 
217 /**
218  *
219  *  rct2: 0x006DF9F0
220  */
CableLiftUpdateArriving()221 void Vehicle::CableLiftUpdateArriving()
222 {
223     sub_state++;
224     if (sub_state >= 64)
225         SetState(Vehicle::Status::MovingToEndOfStation, sub_state);
226 }
227 
CableLiftUpdateTrackMotionForwards()228 bool Vehicle::CableLiftUpdateTrackMotionForwards()
229 {
230     auto curRide = GetRide();
231     if (curRide == nullptr)
232         return false;
233 
234     for (; remaining_distance >= 13962; _vehicleUnkF64E10++)
235     {
236         auto trackType = GetTrackType();
237         if (trackType == TrackElemType::CableLiftHill && track_progress == 160)
238         {
239             _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_1;
240         }
241 
242         uint16_t trackProgress = track_progress + 1;
243 
244         uint16_t trackTotalProgress = GetTrackProgress();
245         if (trackProgress >= trackTotalProgress)
246         {
247             TileElement* trackElement = map_get_track_element_at_of_type_seq(TrackLocation, trackType, 0);
248 
249             CoordsXYE output;
250             int32_t outputZ;
251             int32_t outputDirection;
252 
253             auto input = CoordsXYE{ TrackLocation, trackElement };
254 
255             if (!track_block_get_next(&input, &output, &outputZ, &outputDirection))
256                 return false;
257 
258             if (TrackPitchAndRollEnd(trackType) != TrackPitchAndRollStart(output.element->AsTrack()->GetTrackType()))
259                 return false;
260 
261             TrackLocation = { output, outputZ };
262             SetTrackDirection(outputDirection);
263             SetTrackType(output.element->AsTrack()->GetTrackType());
264             trackProgress = 0;
265         }
266 
267         track_progress = trackProgress;
268         const auto moveInfo = GetMoveInfo();
269         auto unk = CoordsXYZ{ moveInfo->x, moveInfo->y, moveInfo->z } + TrackLocation;
270 
271         uint8_t bx = 0;
272         unk.z += GetRideTypeDescriptor(curRide->type).Heights.VehicleZOffset;
273         if (unk.x != unk_F64E20.x)
274             bx |= (1 << 0);
275         if (unk.y != unk_F64E20.y)
276             bx |= (1 << 1);
277         if (unk.z != unk_F64E20.z)
278             bx |= (1 << 2);
279 
280         remaining_distance -= dword_9A2930[bx];
281         unk_F64E20.x = unk.x;
282         unk_F64E20.y = unk.y;
283         unk_F64E20.z = unk.z;
284 
285         sprite_direction = moveInfo->direction;
286         bank_rotation = moveInfo->bank_rotation;
287         Pitch = moveInfo->Pitch;
288 
289         if (remaining_distance >= 13962)
290         {
291             acceleration += dword_9A2970[Pitch];
292         }
293     }
294     return true;
295 }
296 
CableLiftUpdateTrackMotionBackwards()297 bool Vehicle::CableLiftUpdateTrackMotionBackwards()
298 {
299     auto curRide = GetRide();
300     if (curRide == nullptr)
301         return false;
302 
303     for (; remaining_distance < 0; _vehicleUnkF64E10++)
304     {
305         uint16_t trackProgress = track_progress - 1;
306 
307         if (static_cast<int16_t>(trackProgress) == -1)
308         {
309             auto trackType = GetTrackType();
310             TileElement* trackElement = map_get_track_element_at_of_type_seq(TrackLocation, trackType, 0);
311 
312             auto input = CoordsXYE{ TrackLocation, trackElement };
313             track_begin_end output;
314 
315             if (!track_block_get_previous(input, &output))
316                 return false;
317 
318             if (TrackPitchAndRollStart(trackType) != TrackPitchAndRollEnd(output.begin_element->AsTrack()->GetTrackType()))
319                 return false;
320 
321             TrackLocation = { output.begin_x, output.begin_y, output.begin_z };
322             SetTrackDirection(output.begin_direction);
323             SetTrackType(output.begin_element->AsTrack()->GetTrackType());
324 
325             if (output.begin_element->AsTrack()->GetTrackType() == TrackElemType::EndStation)
326             {
327                 _vehicleMotionTrackFlags = VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION;
328             }
329 
330             uint16_t trackTotalProgress = GetTrackProgress();
331             trackProgress = trackTotalProgress - 1;
332         }
333         track_progress = trackProgress;
334         const auto moveInfo = GetMoveInfo();
335         auto unk = CoordsXYZ{ moveInfo->x, moveInfo->y, moveInfo->z } + TrackLocation;
336 
337         uint8_t bx = 0;
338         unk.z += GetRideTypeDescriptor(curRide->type).Heights.VehicleZOffset;
339         if (unk.x != unk_F64E20.x)
340             bx |= (1 << 0);
341         if (unk.y != unk_F64E20.y)
342             bx |= (1 << 1);
343         if (unk.z != unk_F64E20.z)
344             bx |= (1 << 2);
345 
346         remaining_distance += dword_9A2930[bx];
347         unk_F64E20.x = unk.x;
348         unk_F64E20.y = unk.y;
349         unk_F64E20.z = unk.z;
350 
351         sprite_direction = moveInfo->direction;
352         bank_rotation = moveInfo->bank_rotation;
353         Pitch = moveInfo->Pitch;
354 
355         if (remaining_distance < 0)
356         {
357             acceleration += dword_9A2970[Pitch];
358         }
359     }
360     return true;
361 }
362 
363 /**
364  *
365  *  rct2: 0x006DEF56
366  */
CableLiftUpdateTrackMotion()367 int32_t Vehicle::CableLiftUpdateTrackMotion()
368 {
369     _vehicleF64E2C = 0;
370     gCurrentVehicle = this;
371     _vehicleMotionTrackFlags = 0;
372     _vehicleStationIndex = STATION_INDEX_NULL;
373 
374     velocity += acceleration;
375     _vehicleVelocityF64E08 = velocity;
376     _vehicleVelocityF64E0C = (velocity / 1024) * 42;
377 
378     Vehicle* frontVehicle = this;
379     if (velocity < 0)
380     {
381         frontVehicle = TrainTail();
382     }
383 
384     _vehicleFrontVehicle = frontVehicle;
385 
386     for (Vehicle* vehicle = frontVehicle; vehicle != nullptr;)
387     {
388         vehicle->acceleration = dword_9A2970[vehicle->Pitch];
389         _vehicleUnkF64E10 = 1;
390         vehicle->remaining_distance += _vehicleVelocityF64E0C;
391 
392         if (vehicle->remaining_distance < 0 || vehicle->remaining_distance >= 13962)
393         {
394             unk_F64E20 = vehicle->GetLocation();
395             vehicle->Invalidate();
396 
397             while (true)
398             {
399                 if (vehicle->remaining_distance < 0)
400                 {
401                     if (vehicle->CableLiftUpdateTrackMotionBackwards())
402                     {
403                         break;
404                     }
405 
406                     _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5;
407                     _vehicleVelocityF64E0C -= vehicle->remaining_distance - 13962;
408                     vehicle->remaining_distance = 13962;
409                     vehicle->acceleration += dword_9A2970[vehicle->Pitch];
410                     _vehicleUnkF64E10++;
411                     continue;
412                 }
413 
414                 if (vehicle->CableLiftUpdateTrackMotionForwards())
415                 {
416                     break;
417                 }
418 
419                 _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_5;
420                 _vehicleVelocityF64E0C -= vehicle->remaining_distance + 1;
421                 vehicle->remaining_distance = -1;
422                 vehicle->acceleration += dword_9A2970[vehicle->Pitch];
423                 _vehicleUnkF64E10++;
424             }
425             vehicle->MoveTo(unk_F64E20);
426         }
427         vehicle->acceleration /= _vehicleUnkF64E10;
428         if (_vehicleVelocityF64E08 >= 0)
429         {
430             vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train);
431         }
432         else
433         {
434             if (vehicle == this)
435                 break;
436             vehicle = GetEntity<Vehicle>(vehicle->prev_vehicle_on_ride);
437         }
438     }
439 
440     uint32_t vehicleCount = 0;
441     uint16_t massTotal = 0;
442     int32_t accelerationTotal = 0;
443 
444     for (Vehicle* vehicle = GetEntity<Vehicle>(sprite_index); vehicle != nullptr;
445          vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
446     {
447         vehicleCount++;
448 
449         massTotal += vehicle->mass;
450         accelerationTotal = add_clamp_int32_t(accelerationTotal, vehicle->acceleration);
451     }
452 
453     int32_t newAcceleration = (accelerationTotal / vehicleCount) >> 9;
454     newAcceleration -= velocity >> 12;
455 
456     int32_t edx = velocity >> 8;
457     edx *= edx;
458     if (velocity < 0)
459     {
460         edx = -edx;
461     }
462     edx >>= 4;
463     newAcceleration -= edx / massTotal;
464 
465     acceleration = newAcceleration;
466     return _vehicleMotionTrackFlags;
467 }
468