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 "../Cheats.h"
11 #include "../Context.h"
12 #include "../Game.h"
13 #include "../Input.h"
14 #include "../actions/TrackPlaceAction.h"
15 #include "../audio/audio.h"
16 #include "../interface/Viewport.h"
17 #include "../network/network.h"
18 #include "../paint/VirtualFloor.h"
19 #include "../peep/Staff.h"
20 #include "../ride/RideData.h"
21 #include "../ride/Track.h"
22 #include "../ride/TrackData.h"
23 #include "../util/Math.hpp"
24 #include "../world/Banner.h"
25 #include "../world/Scenery.h"
26 #include "Intent.h"
27
28 #include <iterator>
29 #include <tuple>
30
31 using namespace OpenRCT2::TrackMetaData;
32 bool gDisableErrorWindowSound = false;
33
34 uint64_t _enabledRidePieces;
35 RideConstructionState _rideConstructionState2;
36
37 // This variable is updated separately from ride->num_stations because the latter
38 // is unreliable if currently in station construction mode
39 bool _stationConstructed;
40 bool _deferClose;
41
42 /**
43 *
44 * rct2: 0x006CA162
45 */
place_provisional_track_piece(ride_id_t rideIndex,int32_t trackType,int32_t trackDirection,int32_t liftHillAndAlternativeState,const CoordsXYZ & trackPos)46 money32 place_provisional_track_piece(
47 ride_id_t rideIndex, int32_t trackType, int32_t trackDirection, int32_t liftHillAndAlternativeState,
48 const CoordsXYZ& trackPos)
49 {
50 auto ride = get_ride(rideIndex);
51 if (ride == nullptr)
52 return MONEY32_UNDEFINED;
53
54 ride_construction_remove_ghosts();
55 if (ride->type == RIDE_TYPE_MAZE)
56 {
57 int32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND
58 | GAME_COMMAND_FLAG_GHOST; // 105
59 auto result = maze_set_track(trackPos.x, trackPos.y, trackPos.z, flags, true, 0, rideIndex, GC_SET_MAZE_TRACK_BUILD);
60 if (result == MONEY32_UNDEFINED)
61 return result;
62
63 _unkF440C5 = { trackPos, static_cast<Direction>(trackDirection) };
64 _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_TRACK;
65 viewport_set_visibility(3);
66 if (_currentTrackSlopeEnd != 0)
67 viewport_set_visibility(2);
68
69 // Invalidate previous track piece (we may not be changing height!)
70 virtual_floor_invalidate();
71
72 if (!scenery_tool_is_active())
73 {
74 // Set new virtual floor height.
75 virtual_floor_set_height(trackPos.z);
76 }
77
78 return result;
79 }
80
81 auto trackPlaceAction = TrackPlaceAction(
82 rideIndex, trackType, { trackPos, static_cast<uint8_t>(trackDirection) }, 0, 0, 0, liftHillAndAlternativeState, false);
83 trackPlaceAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST);
84 // This command must not be sent over the network
85 auto res = GameActions::Execute(&trackPlaceAction);
86 if (res->Error != GameActions::Status::Ok)
87 return MONEY32_UNDEFINED;
88
89 int16_t z_begin, z_end;
90 const auto& ted = GetTrackElementDescriptor(trackType);
91 const rct_track_coordinates& coords = ted.Coordinates;
92 if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_NO_TRACK))
93 {
94 z_begin = coords.z_begin;
95 z_end = coords.z_end;
96 }
97 else
98 {
99 z_end = z_begin = coords.z_begin;
100 }
101
102 _unkF440C5 = { trackPos.x, trackPos.y, trackPos.z + z_begin, static_cast<Direction>(trackDirection) };
103 _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_TRACK;
104
105 const auto resultData = res->GetData<TrackPlaceActionResult>();
106 viewport_set_visibility((resultData.GroundFlags & ELEMENT_IS_UNDERGROUND) ? 1 : 3);
107 if (_currentTrackSlopeEnd != 0)
108 viewport_set_visibility(2);
109
110 // Invalidate previous track piece (we may not be changing height!)
111 virtual_floor_invalidate();
112
113 if (!scenery_tool_is_active())
114 {
115 // Set height to where the next track piece would begin
116 virtual_floor_set_height(trackPos.z - z_begin + z_end);
117 }
118
119 return res->Cost;
120 }
121
window_ride_construction_update_state_get_track_element()122 static std::tuple<bool, track_type_t> window_ride_construction_update_state_get_track_element()
123 {
124 auto intent = Intent(INTENT_ACTION_RIDE_CONSTRUCTION_UPDATE_PIECES);
125 context_broadcast_intent(&intent);
126
127 uint8_t startSlope = _previousTrackSlopeEnd;
128 uint8_t endSlope = _currentTrackSlopeEnd;
129 uint8_t startBank = _previousTrackBankEnd;
130 uint8_t endBank = _currentTrackBankEnd;
131
132 if (_rideConstructionState == RideConstructionState::Back)
133 {
134 startSlope = _currentTrackSlopeEnd;
135 endSlope = _previousTrackSlopeEnd;
136 startBank = _currentTrackBankEnd;
137 endBank = _previousTrackBankEnd;
138 }
139
140 auto curve = _currentTrackCurve;
141 if (curve == TrackElemType::None)
142 {
143 return std::make_tuple(false, 0);
144 }
145
146 bool startsDiagonal = (_currentTrackPieceDirection & (1 << 2)) != 0;
147 if (curve == TRACK_CURVE_LEFT_LARGE || curve == TRACK_CURVE_RIGHT_LARGE)
148 {
149 if (_rideConstructionState == RideConstructionState::Back)
150 {
151 startsDiagonal = !startsDiagonal;
152 }
153 }
154
155 if (curve <= 8)
156 {
157 for (uint32_t i = 0; i < std::size(gTrackDescriptors); i++)
158 {
159 const track_descriptor* trackDescriptor = &gTrackDescriptors[i];
160
161 if (trackDescriptor->track_curve != curve)
162 continue;
163 if (trackDescriptor->starts_diagonal != startsDiagonal)
164 continue;
165 if (trackDescriptor->slope_start != startSlope)
166 continue;
167 if (trackDescriptor->slope_end != endSlope)
168 continue;
169 if (trackDescriptor->bank_start != startBank)
170 continue;
171 if (trackDescriptor->bank_end != endBank)
172 continue;
173
174 return std::make_tuple(true, trackDescriptor->track_element);
175 }
176
177 return std::make_tuple(false, 0);
178 }
179
180 switch (curve & 0xFFFF)
181 {
182 case TrackElemType::EndStation:
183 case TrackElemType::SBendLeft:
184 case TrackElemType::SBendRight:
185 if (startSlope != TRACK_SLOPE_NONE || endSlope != TRACK_SLOPE_NONE)
186 {
187 return std::make_tuple(false, 0);
188 }
189
190 if (startBank != TRACK_BANK_NONE || endBank != TRACK_BANK_NONE)
191 {
192 return std::make_tuple(false, 0);
193 }
194
195 return std::make_tuple(true, static_cast<track_type_t>(curve & 0xFFFF));
196
197 case TrackElemType::LeftVerticalLoop:
198 case TrackElemType::RightVerticalLoop:
199 if (startBank != TRACK_BANK_NONE || endBank != TRACK_BANK_NONE)
200 {
201 return std::make_tuple(false, 0);
202 }
203
204 if (_rideConstructionState == RideConstructionState::Back)
205 {
206 if (endSlope != TRACK_SLOPE_DOWN_25)
207 {
208 return std::make_tuple(false, 0);
209 }
210 }
211 else
212 {
213 if (startSlope != TRACK_SLOPE_UP_25)
214 {
215 return std::make_tuple(false, 0);
216 }
217 }
218
219 return std::make_tuple(true, static_cast<track_type_t>(curve & 0xFFFF));
220
221 default:
222 return std::make_tuple(true, static_cast<track_type_t>(curve & 0xFFFF));
223 }
224 }
225
226 /**
227 * rct2: 0x006CA2DF
228 *
229 * @param[out] _trackType (dh)
230 * @param[out] _trackDirection (bh)
231 * @param[out] _rideIndex (dl)
232 * @param[out] _liftHillAndInvertedState (liftHillAndInvertedState)
233 * @param[out] _x (ax)
234 * @param[out] _y (cx)
235 * @param[out] _z (di)
236 * @param[out] _properties (edirs16)
237 * @return (CF)
238 */
window_ride_construction_update_state(int32_t * _trackType,int32_t * _trackDirection,ride_id_t * _rideIndex,int32_t * _liftHillAndInvertedState,CoordsXYZ * _trackPos,int32_t * _properties)239 bool window_ride_construction_update_state(
240 int32_t* _trackType, int32_t* _trackDirection, ride_id_t* _rideIndex, int32_t* _liftHillAndInvertedState,
241 CoordsXYZ* _trackPos, int32_t* _properties)
242 {
243 ride_id_t rideIndex;
244 uint8_t trackDirection;
245 uint16_t x, y, liftHillAndInvertedState, properties;
246
247 auto updated_element = window_ride_construction_update_state_get_track_element();
248 if (!std::get<0>(updated_element))
249 {
250 return true;
251 }
252
253 track_type_t trackType = std::get<1>(updated_element);
254 liftHillAndInvertedState = 0;
255 rideIndex = _currentRideIndex;
256 if (_currentTrackLiftHill & CONSTRUCTION_LIFT_HILL_SELECTED)
257 {
258 liftHillAndInvertedState |= CONSTRUCTION_LIFT_HILL_SELECTED;
259 }
260
261 if (_currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_TYPE)
262 {
263 liftHillAndInvertedState |= CONSTRUCTION_INVERTED_TRACK_SELECTED;
264 }
265
266 auto ride = get_ride(rideIndex);
267 if (ride == nullptr)
268 return true;
269
270 if (_enabledRidePieces & (1ULL << TRACK_SLOPE_STEEP_LONG))
271 {
272 switch (trackType)
273 {
274 case TrackElemType::FlatToUp60:
275 trackType = TrackElemType::FlatToUp60LongBase;
276 break;
277
278 case TrackElemType::Up60ToFlat:
279 trackType = TrackElemType::Up60ToFlatLongBase;
280 break;
281
282 case TrackElemType::FlatToDown60:
283 trackType = TrackElemType::FlatToDown60LongBase;
284 break;
285
286 case TrackElemType::Down60ToFlat:
287 trackType = TrackElemType::Down60ToFlatLongBase;
288 break;
289
290 case TrackElemType::DiagFlatToUp60:
291 case TrackElemType::DiagUp60ToFlat:
292 case TrackElemType::DiagFlatToDown60:
293 case TrackElemType::DiagDown60ToFlat:
294 return true;
295 }
296 }
297
298 const auto& rtd = ride->GetRideTypeDescriptor();
299 if (rtd.HasFlag(RIDE_TYPE_FLAG_TRACK_ELEMENTS_HAVE_TWO_VARIETIES)
300 && _currentTrackAlternative & RIDE_TYPE_ALTERNATIVE_TRACK_PIECES)
301 {
302 auto availablePieces = rtd.CoveredTrackPieces;
303 const auto& ted = GetTrackElementDescriptor(trackType);
304 auto alternativeType = ted.AlternativeType;
305 if (alternativeType != TrackElemType::None && (availablePieces & (1ULL << trackType)))
306 {
307 trackType = alternativeType;
308 if (!gCheatsEnableChainLiftOnAllTrack)
309 liftHillAndInvertedState &= ~CONSTRUCTION_LIFT_HILL_SELECTED;
310 }
311 }
312
313 const auto& ted = GetTrackElementDescriptor(trackType);
314 const rct_track_coordinates& trackCoordinates = ted.Coordinates;
315
316 x = _currentTrackBegin.x;
317 y = _currentTrackBegin.y;
318 auto z = _currentTrackBegin.z;
319 if (_rideConstructionState == RideConstructionState::Back)
320 {
321 z -= trackCoordinates.z_end;
322 trackDirection = _currentTrackPieceDirection ^ 0x02;
323 trackDirection -= trackCoordinates.rotation_end;
324 trackDirection += trackCoordinates.rotation_begin;
325 trackDirection &= 0x03;
326
327 if (trackCoordinates.rotation_begin & (1 << 2))
328 {
329 trackDirection |= 0x04;
330 }
331
332 CoordsXY offsets = { trackCoordinates.x, trackCoordinates.y };
333 CoordsXY coords = { x, y };
334 coords += offsets.Rotate(direction_reverse(trackDirection));
335 x = static_cast<uint16_t>(coords.x);
336 y = static_cast<uint16_t>(coords.y);
337 }
338 else
339 {
340 z -= trackCoordinates.z_begin;
341 trackDirection = _currentTrackPieceDirection;
342 }
343
344 bool turnOffLiftHill = false;
345 if (!(_enabledRidePieces & (1ULL << TRACK_LIFT_HILL_CURVE)))
346 {
347 if (ted.Flags & TRACK_ELEM_FLAG_CURVE_ALLOWS_LIFT)
348 {
349 turnOffLiftHill = true;
350 }
351 }
352
353 if (!(ted.Flags & TRACK_ELEM_FLAG_ALLOW_LIFT_HILL))
354 {
355 turnOffLiftHill = true;
356 }
357
358 if (turnOffLiftHill && !gCheatsEnableChainLiftOnAllTrack)
359 {
360 liftHillAndInvertedState &= ~CONSTRUCTION_LIFT_HILL_SELECTED;
361 _currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED;
362
363 if (trackType == TrackElemType::LeftCurvedLiftHill || trackType == TrackElemType::RightCurvedLiftHill)
364 {
365 liftHillAndInvertedState |= CONSTRUCTION_LIFT_HILL_SELECTED;
366 }
367 }
368
369 if (TrackTypeHasSpeedSetting(trackType))
370 {
371 properties = _currentBrakeSpeed2;
372 }
373 else
374 {
375 properties = _currentSeatRotationAngle << 12;
376 }
377
378 if (_trackType != nullptr)
379 *_trackType = trackType;
380 if (_trackDirection != nullptr)
381 *_trackDirection = trackDirection;
382 if (_rideIndex != nullptr)
383 *_rideIndex = rideIndex;
384 if (_liftHillAndInvertedState != nullptr)
385 *_liftHillAndInvertedState = liftHillAndInvertedState;
386 if (_trackPos != nullptr)
387 *_trackPos = { x, y, z };
388 if (_properties != nullptr)
389 *_properties = properties;
390
391 return false;
392 }
393
window_ride_construction_do_entrance_exit_check()394 void window_ride_construction_do_entrance_exit_check()
395 {
396 auto w = window_find_by_class(WC_RIDE_CONSTRUCTION);
397 auto ride = get_ride(_currentRideIndex);
398 if (w == nullptr || ride == nullptr)
399 {
400 return;
401 }
402
403 if (_rideConstructionState == RideConstructionState::State0)
404 {
405 w = window_find_by_class(WC_RIDE_CONSTRUCTION);
406 if (w != nullptr)
407 {
408 if (!ride_are_all_possible_entrances_and_exits_built(ride))
409 {
410 window_event_mouse_up_call(w, WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE);
411 }
412 else
413 {
414 _deferClose = true;
415 }
416 }
417 }
418 }
419
window_ride_construction_do_station_check()420 void window_ride_construction_do_station_check()
421 {
422 auto ride = get_ride(_currentRideIndex);
423 if (ride != nullptr)
424 {
425 _stationConstructed = ride->num_stations != 0;
426 }
427 }
428
window_ride_construction_mouseup_demolish_next_piece(const CoordsXYZD & piecePos,int32_t type)429 void window_ride_construction_mouseup_demolish_next_piece(const CoordsXYZD& piecePos, int32_t type)
430 {
431 if (gGotoStartPlacementMode)
432 {
433 _currentTrackBegin.z = floor2(piecePos.z, COORDS_Z_STEP);
434 _rideConstructionState = RideConstructionState::Front;
435 _currentTrackSelectionFlags = 0;
436 _currentTrackPieceDirection = piecePos.direction & 3;
437 auto savedCurrentTrackCurve = _currentTrackCurve;
438 int32_t savedPreviousTrackSlopeEnd = _previousTrackSlopeEnd;
439 int32_t savedCurrentTrackSlopeEnd = _currentTrackSlopeEnd;
440 int32_t savedPreviousTrackBankEnd = _previousTrackBankEnd;
441 int32_t savedCurrentTrackBankEnd = _currentTrackBankEnd;
442 int32_t savedCurrentTrackAlternative = _currentTrackAlternative;
443 int32_t savedCurrentTrackLiftHill = _currentTrackLiftHill;
444 ride_construction_set_default_next_piece();
445 window_ride_construction_update_active_elements();
446 auto ride = get_ride(_currentRideIndex);
447 if (!ride_try_get_origin_element(ride, nullptr))
448 {
449 ride_initialise_construction_window(ride);
450 _currentTrackPieceDirection = piecePos.direction & 3;
451 if (!(savedCurrentTrackCurve & RideConstructionSpecialPieceSelected))
452 {
453 _currentTrackCurve = savedCurrentTrackCurve;
454 _previousTrackSlopeEnd = savedPreviousTrackSlopeEnd;
455 _currentTrackSlopeEnd = savedCurrentTrackSlopeEnd;
456 _previousTrackBankEnd = savedPreviousTrackBankEnd;
457 _currentTrackBankEnd = savedCurrentTrackBankEnd;
458 _currentTrackAlternative = savedCurrentTrackAlternative;
459 _currentTrackLiftHill = savedCurrentTrackLiftHill;
460 window_ride_construction_update_active_elements();
461 }
462 }
463 }
464 else
465 {
466 if (_rideConstructionState2 == RideConstructionState::Selected
467 || _rideConstructionState2 == RideConstructionState::Front)
468 {
469 if (type == TrackElemType::MiddleStation || type == TrackElemType::BeginStation)
470 {
471 type = TrackElemType::EndStation;
472 }
473 }
474 if (_rideConstructionState2 == RideConstructionState::Back)
475 {
476 if (type == TrackElemType::MiddleStation)
477 {
478 type = TrackElemType::BeginStation;
479 }
480 }
481 if (network_get_mode() == NETWORK_MODE_CLIENT)
482 {
483 // rideConstructionState needs to be set again to the proper value, this only affects the client
484 _rideConstructionState = RideConstructionState::Selected;
485 }
486 _currentTrackBegin = piecePos;
487 _currentTrackPieceDirection = piecePos.direction;
488 _currentTrackPieceType = type;
489 _currentTrackSelectionFlags = 0;
490 if (_rideConstructionState2 == RideConstructionState::Front)
491 {
492 ride_select_next_section();
493 }
494 else if (_rideConstructionState2 == RideConstructionState::Back)
495 {
496 ride_select_previous_section();
497 }
498 window_ride_construction_update_active_elements();
499 }
500 }
501
502 /**
503 *
504 * rct2: 0x006C84CE
505 */
window_ride_construction_update_active_elements()506 void window_ride_construction_update_active_elements()
507 {
508 auto intent = Intent(INTENT_ACTION_RIDE_CONSTRUCTION_UPDATE_ACTIVE_ELEMENTS);
509 context_broadcast_intent(&intent);
510 }
511
512 /**
513 *
514 * rct2: 0x0066DB3D
515 */
scenery_tool_is_active()516 bool scenery_tool_is_active()
517 {
518 int32_t toolWindowClassification = gCurrentToolWidget.window_classification;
519 rct_widgetindex toolWidgetIndex = gCurrentToolWidget.widget_index;
520 if (input_test_flag(INPUT_FLAG_TOOL_ACTIVE))
521 if (toolWindowClassification == WC_TOP_TOOLBAR && toolWidgetIndex == WC_TOP_TOOLBAR__WIDX_SCENERY)
522 return true;
523
524 return false;
525 }
526
init_scenery()527 void init_scenery()
528 {
529 auto intent = Intent(INTENT_ACTION_INIT_SCENERY);
530 context_broadcast_intent(&intent);
531 }
532
scenery_set_default_placement_configuration()533 void scenery_set_default_placement_configuration()
534 {
535 auto intent = Intent(INTENT_ACTION_SET_DEFAULT_SCENERY_CONFIG);
536 context_broadcast_intent(&intent);
537 }
538