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