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 "Station.h"
11 
12 #include "../Game.h"
13 #include "../peep/Guest.h"
14 #include "../scenario/Scenario.h"
15 #include "../world/Location.hpp"
16 #include "Track.h"
17 #include "Vehicle.h"
18 
19 static void ride_update_station_blocksection(Ride* ride, StationIndex stationIndex);
20 static void ride_update_station_dodgems(Ride* ride, StationIndex stationIndex);
21 static void ride_update_station_normal(Ride* ride, StationIndex stationIndex);
22 static void ride_update_station_race(Ride* ride, StationIndex stationIndex);
23 static void ride_race_init_vehicle_speeds(Ride* ride);
24 static void ride_invalidate_station_start(Ride* ride, StationIndex stationIndex, bool greenLight);
25 
26 /**
27  *
28  *  rct2: 0x006ABFFB
29  */
ride_update_station(Ride * ride,StationIndex stationIndex)30 void ride_update_station(Ride* ride, StationIndex stationIndex)
31 {
32     if (ride->stations[stationIndex].Start.IsNull())
33         return;
34 
35     switch (ride->mode)
36     {
37         case RideMode::Race:
38             ride_update_station_race(ride, stationIndex);
39             break;
40         case RideMode::Dodgems:
41             ride_update_station_dodgems(ride, stationIndex);
42             break;
43         case RideMode::ContinuousCircuitBlockSectioned:
44         case RideMode::PoweredLaunchBlockSectioned:
45             ride_update_station_blocksection(ride, stationIndex);
46             break;
47         default:
48             ride_update_station_normal(ride, stationIndex);
49             break;
50     }
51 }
52 
53 /**
54  *
55  *  rct2: 0x006AC0A1
56  */
ride_update_station_blocksection(Ride * ride,StationIndex stationIndex)57 static void ride_update_station_blocksection(Ride* ride, StationIndex stationIndex)
58 {
59     TileElement* tileElement = ride_get_station_start_track_element(ride, stationIndex);
60 
61     if ((ride->status == RideStatus::Closed && ride->num_riders == 0)
62         || (tileElement != nullptr && tileElement->AsTrack()->BlockBrakeClosed()))
63     {
64         ride->stations[stationIndex].Depart &= ~STATION_DEPART_FLAG;
65 
66         if ((ride->stations[stationIndex].Depart & STATION_DEPART_FLAG)
67             || (tileElement != nullptr && tileElement->AsTrack()->HasGreenLight()))
68             ride_invalidate_station_start(ride, stationIndex, false);
69     }
70     else
71     {
72         if (!(ride->stations[stationIndex].Depart & STATION_DEPART_FLAG))
73         {
74             ride->stations[stationIndex].Depart |= STATION_DEPART_FLAG;
75             ride_invalidate_station_start(ride, stationIndex, true);
76         }
77         else if (tileElement != nullptr && tileElement->AsTrack()->HasGreenLight())
78         {
79             ride_invalidate_station_start(ride, stationIndex, true);
80         }
81     }
82 }
83 
84 /**
85  *
86  *  rct2: 0x006AC12B
87  */
ride_update_station_dodgems(Ride * ride,StationIndex stationIndex)88 static void ride_update_station_dodgems(Ride* ride, StationIndex stationIndex)
89 {
90     // Change of station depart flag should really call invalidate_station_start
91     // but since dodgems do not have station lights there is no point.
92     if (ride->status == RideStatus::Closed || (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)))
93     {
94         ride->stations[stationIndex].Depart &= ~STATION_DEPART_FLAG;
95         return;
96     }
97 
98     if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING)
99     {
100         int32_t dx = ride->time_limit * 32;
101         int32_t dh = (dx >> 8) & 0xFF;
102         for (size_t i = 0; i < ride->num_vehicles; i++)
103         {
104             Vehicle* vehicle = GetEntity<Vehicle>(ride->vehicles[i]);
105             if (vehicle == nullptr)
106                 continue;
107 
108             if (vehicle->var_CE < dh)
109                 continue;
110 
111             // End match
112             ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING;
113             ride->stations[stationIndex].Depart &= ~STATION_DEPART_FLAG;
114             return;
115         }
116 
117         // Continue match
118         ride->stations[stationIndex].Depart |= STATION_DEPART_FLAG;
119     }
120     else
121     {
122         // Check if all vehicles are ready to go
123         for (size_t i = 0; i < ride->num_vehicles; i++)
124         {
125             Vehicle* vehicle = GetEntity<Vehicle>(ride->vehicles[i]);
126             if (vehicle == nullptr)
127                 continue;
128 
129             if (vehicle->status != Vehicle::Status::WaitingToDepart)
130             {
131                 ride->stations[stationIndex].Depart &= ~STATION_DEPART_FLAG;
132                 return;
133             }
134         }
135 
136         // Begin the match
137         ride->lifecycle_flags |= RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING;
138         ride->stations[stationIndex].Depart |= STATION_DEPART_FLAG;
139         ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
140     }
141 }
142 
143 /**
144  *
145  *  rct2: 0x006AC02C
146  */
ride_update_station_normal(Ride * ride,StationIndex stationIndex)147 static void ride_update_station_normal(Ride* ride, StationIndex stationIndex)
148 {
149     int32_t time = ride->stations[stationIndex].Depart & STATION_DEPART_MASK;
150     if ((ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))
151         || (ride->status == RideStatus::Closed && ride->num_riders == 0))
152     {
153         if (time != 0 && time != 127 && !(gCurrentTicks & 7))
154             time--;
155 
156         ride->stations[stationIndex].Depart = time;
157         ride_invalidate_station_start(ride, stationIndex, false);
158     }
159     else
160     {
161         if (time == 0)
162         {
163             ride->stations[stationIndex].Depart |= STATION_DEPART_FLAG;
164             ride_invalidate_station_start(ride, stationIndex, true);
165         }
166         else
167         {
168             if (time != 127 && !(gCurrentTicks & 31))
169                 time--;
170 
171             ride->stations[stationIndex].Depart = time;
172             ride_invalidate_station_start(ride, stationIndex, false);
173         }
174     }
175 }
176 
177 /**
178  *
179  *  rct2: 0x006AC1DF
180  */
ride_update_station_race(Ride * ride,StationIndex stationIndex)181 static void ride_update_station_race(Ride* ride, StationIndex stationIndex)
182 {
183     if (ride->status == RideStatus::Closed || (ride->lifecycle_flags & (RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED)))
184     {
185         if (ride->stations[stationIndex].Depart & STATION_DEPART_FLAG)
186         {
187             ride->stations[stationIndex].Depart &= ~STATION_DEPART_FLAG;
188             ride_invalidate_station_start(ride, stationIndex, false);
189         }
190         return;
191     }
192 
193     if (ride->lifecycle_flags & RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING)
194     {
195         int32_t numLaps = ride->num_laps;
196 
197         for (size_t i = 0; i < ride->num_vehicles; i++)
198         {
199             Vehicle* vehicle = GetEntity<Vehicle>(ride->vehicles[i]);
200             if (vehicle == nullptr)
201                 continue;
202 
203             if (vehicle->status != Vehicle::Status::WaitingToDepart && vehicle->num_laps >= numLaps)
204             {
205                 // Found a winner
206                 if (vehicle->num_peeps != 0)
207                 {
208                     auto* peep = GetEntity<Guest>(vehicle->peep[0]);
209                     if (peep != nullptr)
210                     {
211                         ride->race_winner = peep->sprite_index;
212                         ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
213                     }
214                 }
215 
216                 // Race is over
217                 ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING;
218                 if (ride->stations[stationIndex].Depart & STATION_DEPART_FLAG)
219                 {
220                     ride->stations[stationIndex].Depart &= ~STATION_DEPART_FLAG;
221                     ride_invalidate_station_start(ride, stationIndex, false);
222                 }
223                 return;
224             }
225         }
226 
227         // Continue racing
228         ride->stations[stationIndex].Depart |= STATION_DEPART_FLAG;
229     }
230     else
231     {
232         // Check if all vehicles are ready to go
233         for (size_t i = 0; i < ride->num_vehicles; i++)
234         {
235             Vehicle* vehicle = GetEntity<Vehicle>(ride->vehicles[i]);
236             if (vehicle == nullptr)
237                 continue;
238 
239             if (vehicle->status != Vehicle::Status::WaitingToDepart && vehicle->status != Vehicle::Status::Departing)
240             {
241                 if (ride->stations[stationIndex].Depart & STATION_DEPART_FLAG)
242                 {
243                     ride->stations[stationIndex].Depart &= ~STATION_DEPART_FLAG;
244                     ride_invalidate_station_start(ride, stationIndex, false);
245                 }
246                 return;
247             }
248         }
249 
250         // Begin the race
251         ride_race_init_vehicle_speeds(ride);
252         ride->lifecycle_flags |= RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING;
253         if (!(ride->stations[stationIndex].Depart & STATION_DEPART_FLAG))
254         {
255             ride->stations[stationIndex].Depart |= STATION_DEPART_FLAG;
256             ride_invalidate_station_start(ride, stationIndex, true);
257         }
258         ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
259     }
260 }
261 
262 /**
263  *
264  *  rct2: 0x006AC988
265  * set the speed of the go kart type vehicle at the start to a random value or alter if peep name is an easter egg
266  * @param ride (esi)
267  */
ride_race_init_vehicle_speeds(Ride * ride)268 static void ride_race_init_vehicle_speeds(Ride* ride)
269 {
270     for (size_t i = 0; i < ride->num_vehicles; i++)
271     {
272         Vehicle* vehicle = GetEntity<Vehicle>(ride->vehicles[i]);
273         if (vehicle == nullptr)
274             continue;
275 
276         vehicle->ClearUpdateFlag(VEHICLE_UPDATE_FLAG_6);
277 
278         rct_ride_entry* rideEntry = vehicle->GetRideEntry();
279 
280         vehicle->speed = (scenario_rand() & 16) - 8 + rideEntry->vehicles[vehicle->vehicle_type].powered_max_speed;
281 
282         if (vehicle->num_peeps != 0)
283         {
284             auto* guest = GetEntity<Guest>(vehicle->peep[0]);
285 
286             // Easter egg names should only work on guests
287             if (guest != nullptr)
288             {
289                 switch (guest->GetEasterEggNameId())
290                 {
291                     case EASTEREGG_PEEP_NAME_MICHAEL_SCHUMACHER:
292                         vehicle->speed += 35;
293                         break;
294                     case EASTEREGG_PEEP_NAME_JACQUES_VILLENEUVE:
295                         vehicle->speed += 25;
296                         break;
297                     case EASTEREGG_PEEP_NAME_DAMON_HILL:
298                         vehicle->speed += 55;
299                         break;
300                     case EASTEREGG_PEEP_NAME_CHRIS_SAWYER:
301                         vehicle->speed += 14;
302                         break;
303                     case EASTEREGG_PEEP_NAME_MR_BEAN:
304                         vehicle->speed = 9;
305                         break;
306                 }
307             }
308         }
309     }
310 }
311 
312 /**
313  *
314  *  rct2: 0x006AC2C7
315  */
ride_invalidate_station_start(Ride * ride,StationIndex stationIndex,bool greenLight)316 static void ride_invalidate_station_start(Ride* ride, StationIndex stationIndex, bool greenLight)
317 {
318     auto startPos = ride->stations[stationIndex].Start;
319     TileElement* tileElement = ride_get_station_start_track_element(ride, stationIndex);
320 
321     // If no station track found return
322     if (tileElement == nullptr)
323         return;
324 
325     tileElement->AsTrack()->SetHasGreenLight(greenLight);
326 
327     // Invalidate map tile
328     map_invalidate_tile_zoom1({ startPos, tileElement->GetBaseZ(), tileElement->GetClearanceZ() });
329 }
330 
ride_get_station_start_track_element(const Ride * ride,StationIndex stationIndex)331 TileElement* ride_get_station_start_track_element(const Ride* ride, StationIndex stationIndex)
332 {
333     auto stationStart = ride->stations[stationIndex].GetStart();
334 
335     // Find the station track element
336     TileElement* tileElement = map_get_first_element_at(stationStart);
337     if (tileElement == nullptr)
338         return nullptr;
339     do
340     {
341         if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK && stationStart.z == tileElement->GetBaseZ())
342             return tileElement;
343 
344     } while (!(tileElement++)->IsLastForTile());
345 
346     return nullptr;
347 }
348 
ride_get_station_exit_element(const CoordsXYZ & elementPos)349 TileElement* ride_get_station_exit_element(const CoordsXYZ& elementPos)
350 {
351     // Find the station track element
352     TileElement* tileElement = map_get_first_element_at(elementPos);
353     if (tileElement == nullptr)
354         return nullptr;
355     do
356     {
357         if (tileElement == nullptr)
358             break;
359         if (tileElement->GetType() == TILE_ELEMENT_TYPE_ENTRANCE && elementPos.z == tileElement->GetBaseZ())
360             return tileElement;
361     } while (!(tileElement++)->IsLastForTile());
362 
363     return nullptr;
364 }
365 
ride_get_first_valid_station_exit(Ride * ride)366 StationIndex ride_get_first_valid_station_exit(Ride* ride)
367 {
368     for (StationIndex i = 0; i < MAX_STATIONS; i++)
369     {
370         if (!ride->stations[i].Exit.IsNull())
371         {
372             return i;
373         }
374     }
375     return STATION_INDEX_NULL;
376 }
377 
ride_get_first_valid_station_start(const Ride * ride)378 StationIndex ride_get_first_valid_station_start(const Ride* ride)
379 {
380     for (StationIndex i = 0; i < MAX_STATIONS; i++)
381     {
382         if (!ride->stations[i].Start.IsNull())
383         {
384             return i;
385         }
386     }
387     return STATION_INDEX_NULL;
388 }
389 
ride_get_first_empty_station_start(const Ride * ride)390 StationIndex ride_get_first_empty_station_start(const Ride* ride)
391 {
392     for (StationIndex i = 0; i < MAX_STATIONS; i++)
393     {
394         if (ride->stations[i].Start.IsNull())
395         {
396             return i;
397         }
398     }
399     return STATION_INDEX_NULL;
400 }
401 
ride_get_entrance_location(const Ride * ride,const StationIndex stationIndex)402 TileCoordsXYZD ride_get_entrance_location(const Ride* ride, const StationIndex stationIndex)
403 {
404     return ride->stations[stationIndex].Entrance;
405 }
406 
ride_get_exit_location(const Ride * ride,const StationIndex stationIndex)407 TileCoordsXYZD ride_get_exit_location(const Ride* ride, const StationIndex stationIndex)
408 {
409     return ride->stations[stationIndex].Exit;
410 }
411 
ride_clear_entrance_location(Ride * ride,const StationIndex stationIndex)412 void ride_clear_entrance_location(Ride* ride, const StationIndex stationIndex)
413 {
414     ride->stations[stationIndex].Entrance.SetNull();
415 }
416 
ride_clear_exit_location(Ride * ride,const StationIndex stationIndex)417 void ride_clear_exit_location(Ride* ride, const StationIndex stationIndex)
418 {
419     ride->stations[stationIndex].Exit.SetNull();
420 }
421 
ride_set_entrance_location(Ride * ride,const StationIndex stationIndex,const TileCoordsXYZD & location)422 void ride_set_entrance_location(Ride* ride, const StationIndex stationIndex, const TileCoordsXYZD& location)
423 {
424     ride->stations[stationIndex].Entrance = location;
425 }
426 
ride_set_exit_location(Ride * ride,const StationIndex stationIndex,const TileCoordsXYZD & location)427 void ride_set_exit_location(Ride* ride, const StationIndex stationIndex, const TileCoordsXYZD& location)
428 {
429     ride->stations[stationIndex].Exit = location;
430 }
431 
GetBaseZ() const432 int32_t RideStation::GetBaseZ() const
433 {
434     return Height * COORDS_Z_STEP;
435 }
436 
SetBaseZ(int32_t newZ)437 void RideStation::SetBaseZ(int32_t newZ)
438 {
439     Height = newZ / COORDS_Z_STEP;
440 }
441 
GetStart() const442 CoordsXYZ RideStation::GetStart() const
443 {
444     return { Start, GetBaseZ() };
445 }
446