Add and implement simulation mode for rides

This commit is contained in:
Ted John 2019-05-07 20:14:21 +01:00
parent 46889b5381
commit c4e18e570e
17 changed files with 185 additions and 65 deletions

View File

@ -3770,6 +3770,9 @@ STR_6319 :{WINDOW_COLOUR_2}Block Brake Closed
STR_6320 :{WINDOW_COLOUR_2}Indestructible
STR_6321 :{WINDOW_COLOUR_2}Addition is broken
STR_6322 :{WINDOW_COLOUR_2}Sprite Id: {BLACK}{INT32}
STR_6323 :Simulating
STR_6324 :Simulate
STR_6325 :{SMALLFONT}{BLACK}Simulate ride/attraction
#############
# Scenarios #

View File

@ -1,5 +1,6 @@
0.2.2+ (in development)
------------------------------------------------------------------------
- Feature: [#485] Rides can now be simulated with ghost trains during construction.
- Feature: [#2339] Find local servers automatically when fetching servers.
- Feature: [#7296] Allow assigning a keyboard shortcut for the scenery picker.
- Feature: [#8029] Add the Hungarian Forint (HUF) to the list of available currencies.

View File

@ -98,6 +98,7 @@ enum {
WIDX_LOCATE,
WIDX_DEMOLISH,
WIDX_CLOSE_LIGHT,
WIDX_SIMULATE_LIGHT,
WIDX_TEST_LIGHT,
WIDX_OPEN_LIGHT,
WIDX_RIDE_TYPE,
@ -231,6 +232,7 @@ static rct_widget window_ride_main_widgets[] = {
{ WWT_FLATBTN, 1, 291, 314, 118, 141, SPR_LOCATE, STR_LOCATE_SUBJECT_TIP },
{ WWT_FLATBTN, 1, 291, 314, 142, 165, SPR_DEMOLISH, STR_DEMOLISH_RIDE_TIP },
{ WWT_IMGBTN, 1, 296, 309, 48, 61, SPR_G2_RCT1_CLOSE_BUTTON_0, STR_CLOSE_RIDE_TIP },
{ WWT_IMGBTN, 1, 296, 309, 62, 75, SPR_G2_RCT1_TEST_BUTTON_0, STR_SIMULATE_RIDE_TIP },
{ WWT_IMGBTN, 1, 296, 309, 62, 75, SPR_G2_RCT1_TEST_BUTTON_0, STR_TEST_RIDE_TIP },
{ WWT_IMGBTN, 1, 296, 309, 76, 89, SPR_G2_RCT1_OPEN_BUTTON_0, STR_OPEN_RIDE_TIP },
{ WWT_DROPDOWN, 1, 3, 307, 180, 191, STR_ARG_6_STRINGID, STR_NONE },
@ -397,6 +399,7 @@ static constexpr const uint64_t window_ride_page_enabled_widgets[] = {
(1ULL << WIDX_LOCATE) |
(1ULL << WIDX_DEMOLISH) |
(1ULL << WIDX_CLOSE_LIGHT) |
(1ULL << WIDX_SIMULATE_LIGHT) |
(1ULL << WIDX_TEST_LIGHT) |
(1ULL << WIDX_OPEN_LIGHT) |
(1ULL << WIDX_RIDE_TYPE) |
@ -2024,6 +2027,7 @@ static void window_ride_main_mouseup(rct_window* w, rct_widgetindex widgetIndex)
context_open_detail_window(WD_DEMOLISH_RIDE, w->number);
break;
case WIDX_CLOSE_LIGHT:
case WIDX_SIMULATE_LIGHT:
case WIDX_TEST_LIGHT:
case WIDX_OPEN_LIGHT:
switch (widgetIndex)
@ -2032,6 +2036,9 @@ static void window_ride_main_mouseup(rct_window* w, rct_widgetindex widgetIndex)
case WIDX_CLOSE_LIGHT:
status = RIDE_STATUS_CLOSED;
break;
case WIDX_SIMULATE_LIGHT:
status = RIDE_STATUS_SIMULATING;
break;
case WIDX_TEST_LIGHT:
status = RIDE_STATUS_TESTING;
break;
@ -2060,7 +2067,7 @@ static void window_ride_main_resize(rct_window* w)
int32_t minHeight = 180 + offset;
if (theme_get_flags() & UITHEME_FLAG_USE_LIGHTS_RIDE)
minHeight = 200 + offset + RCT1_LIGHT_OFFSET
- (ride_type_has_flag(get_ride(w->number)->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? 14 : 0);
- (ride_type_has_flag(get_ride(w->number)->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? 14 * 2 : 0);
window_set_resize(w, 316, minHeight, 500, 450);
window_ride_init_viewport(w);
}
@ -2130,7 +2137,7 @@ static void window_ride_show_view_dropdown(rct_window* w, rct_widget* widget)
static void window_ride_show_open_dropdown(rct_window* w, rct_widget* widget)
{
Ride* ride;
int32_t numItems, highlightedIndex = 0, checkedIndex;
int32_t numItems, highlightedIndex = 0;
ride = get_ride(w->number);
@ -2141,6 +2148,10 @@ static void window_ride_show_open_dropdown(rct_window* w, rct_widget* widget)
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE))
{
gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[numItems] = STR_SIMULATE_RIDE;
numItems++;
gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[numItems] = STR_TEST_RIDE;
numItems++;
@ -2153,47 +2164,52 @@ static void window_ride_show_open_dropdown(rct_window* w, rct_widget* widget)
window_dropdown_show_text(
w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[1], 0, numItems);
checkedIndex = ride->status;
auto checkedIndex = -1;
switch (ride->status)
{
case RIDE_STATUS_CLOSED:
checkedIndex = 0;
highlightedIndex = 0;
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)
|| (ride->lifecycle_flags & RIDE_LIFECYCLE_HAS_STALLED_VEHICLE))
break;
highlightedIndex = 3;
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE))
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED))
highlightedIndex = 2;
break;
case RIDE_STATUS_SIMULATING:
checkedIndex = 1;
highlightedIndex = 2;
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE))
break;
if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)
break;
highlightedIndex = 1;
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED))
highlightedIndex = 0;
break;
case RIDE_STATUS_TESTING:
highlightedIndex = 2;
if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)
break;
highlightedIndex = 0;
checkedIndex = 2;
highlightedIndex = 3;
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED))
highlightedIndex = 3;
break;
case RIDE_STATUS_OPEN:
checkedIndex = 3;
highlightedIndex = 0;
break;
}
if (checkedIndex != RIDE_STATUS_CLOSED)
checkedIndex = 3 - checkedIndex;
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE))
{
if (checkedIndex != 0)
checkedIndex--;
if (highlightedIndex != 0)
highlightedIndex--;
// Open item is now index 1
if (checkedIndex == 3)
checkedIndex = 1;
if (highlightedIndex == 3)
highlightedIndex = 1;
}
dropdown_set_checked(checkedIndex, true);
if (checkedIndex != -1)
{
dropdown_set_checked(checkedIndex, true);
}
gDropdownDefaultIndex = highlightedIndex;
}
@ -2412,7 +2428,7 @@ static void window_ride_main_dropdown(rct_window* w, rct_widgetindex widgetIndex
ride = get_ride(w->number);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) && dropdownIndex != 0)
dropdownIndex++;
dropdownIndex += 2;
switch (dropdownIndex)
{
@ -2420,9 +2436,12 @@ static void window_ride_main_dropdown(rct_window* w, rct_widgetindex widgetIndex
status = RIDE_STATUS_CLOSED;
break;
case 1:
status = RIDE_STATUS_TESTING;
status = RIDE_STATUS_SIMULATING;
break;
case 2:
status = RIDE_STATUS_TESTING;
break;
case 3:
status = RIDE_STATUS_OPEN;
break;
}
@ -2533,11 +2552,14 @@ static void window_ride_main_invalidate(rct_window* w)
SPR_CLOSED,
SPR_OPEN,
SPR_TESTING,
SPR_TESTING | (COLOUR_WHITE << 19 | COLOUR_WHITE << 24 | IMAGE_TYPE_REMAP),
};
window_ride_main_widgets[WIDX_OPEN].image = spriteIds[ride->status];
window_ride_main_widgets[WIDX_CLOSE_LIGHT].image = SPR_G2_RCT1_CLOSE_BUTTON_0 + (ride->status == RIDE_STATUS_CLOSED) * 2
+ widget_is_pressed(w, WIDX_CLOSE_LIGHT);
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].image = SPR_G2_RCT1_TEST_BUTTON_0
+ (ride->status == RIDE_STATUS_SIMULATING) * 2 + widget_is_pressed(w, WIDX_SIMULATE_LIGHT);
window_ride_main_widgets[WIDX_TEST_LIGHT].image = SPR_G2_RCT1_TEST_BUTTON_0 + (ride->status == RIDE_STATUS_TESTING) * 2
+ widget_is_pressed(w, WIDX_TEST_LIGHT);
window_ride_main_widgets[WIDX_OPEN_LIGHT].image = SPR_G2_RCT1_OPEN_BUTTON_0 + (ride->status == RIDE_STATUS_OPEN) * 2
@ -2580,11 +2602,19 @@ static void window_ride_main_invalidate(rct_window* w)
{
window_ride_main_widgets[WIDX_OPEN].type = WWT_EMPTY;
window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WWT_IMGBTN;
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type
= (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? WWT_EMPTY : WWT_IMGBTN);
window_ride_main_widgets[WIDX_TEST_LIGHT].type
= (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? WWT_EMPTY : WWT_IMGBTN);
window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WWT_IMGBTN;
height = 62;
if (window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type != WWT_EMPTY)
{
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].top = height;
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].bottom = height + 13;
height += 14;
}
if (window_ride_main_widgets[WIDX_TEST_LIGHT].type != WWT_EMPTY)
{
window_ride_main_widgets[WIDX_TEST_LIGHT].top = height;
@ -2603,6 +2633,7 @@ static void window_ride_main_invalidate(rct_window* w)
{
window_ride_main_widgets[WIDX_OPEN].type = WWT_FLATBTN;
window_ride_main_widgets[WIDX_CLOSE_LIGHT].type = WWT_EMPTY;
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WWT_EMPTY;
window_ride_main_widgets[WIDX_TEST_LIGHT].type = WWT_EMPTY;
window_ride_main_widgets[WIDX_OPEN_LIGHT].type = WWT_EMPTY;
height = 46;

View File

@ -2026,7 +2026,7 @@ static void window_ride_construction_update(rct_window* w)
// Close construction window if ride is not closed,
// editing ride while open will cause many issues until properly handled
if (ride->status != RIDE_STATUS_CLOSED)
if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING)
{
window_close(w);
return;

View File

@ -78,7 +78,7 @@ public:
if (_modifyType == RIDE_MODIFY_RENEW)
{
if (ride->status != RIDE_STATUS_CLOSED)
if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING)
{
return std::make_unique<GameActionResult>(
GA_ERROR::DISALLOWED, STR_CANT_REFURBISH_RIDE, STR_MUST_BE_CLOSED_FIRST);

View File

@ -79,7 +79,7 @@ public:
return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle);
}
if (ride->status != RIDE_STATUS_CLOSED)
if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING)
{
return MakeResult(GA_ERROR::NOT_CLOSED, errorTitle, STR_MUST_BE_CLOSED_FIRST);
}

View File

@ -60,7 +60,7 @@ public:
return std::make_unique<GameActionResult>(GA_ERROR::INVALID_PARAMETERS, STR_NONE);
}
if (ride->status != RIDE_STATUS_CLOSED)
if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING)
{
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_MUST_BE_CLOSED_FIRST);
}

View File

@ -82,7 +82,7 @@ public:
GA_ERROR::DISALLOWED, STR_CANT_CHANGE_OPERATING_MODE, STR_HAS_BROKEN_DOWN_AND_REQUIRES_FIXING);
}
if (ride->status != RIDE_STATUS_CLOSED)
if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING)
{
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_CHANGE_OPERATING_MODE, STR_MUST_BE_CLOSED_FIRST);
}

View File

@ -73,9 +73,9 @@ public:
if (_status != ride->status)
{
if (_status == RIDE_STATUS_TESTING)
if (_status == RIDE_STATUS_TESTING || _status == RIDE_STATUS_SIMULATING)
{
if (!ride_is_valid_for_test(ride, _status == RIDE_STATUS_OPEN, 0))
if (!ride_is_valid_for_test(ride, _status, 0))
{
res->Error = GA_ERROR::UNKNOWN;
res->ErrorMessage = gGameCommandErrorText;
@ -123,7 +123,7 @@ public:
switch (_status)
{
case RIDE_STATUS_CLOSED:
if (ride->status == _status)
if (ride->status == _status || ride->status == RIDE_STATUS_SIMULATING)
{
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN))
{
@ -139,6 +139,29 @@ public:
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
window_invalidate_by_number(WC_RIDE, _rideIndex);
break;
case RIDE_STATUS_SIMULATING:
{
ride->lifecycle_flags &= ~RIDE_LIFECYCLE_CRASHED;
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
if (!ride_is_valid_for_test(ride, _status, 1))
{
res->Error = GA_ERROR::UNKNOWN;
res->ErrorMessage = gGameCommandErrorText;
return res;
}
ride->status = _status;
ride->lifecycle_flags &= ~RIDE_LIFECYCLE_PASS_STATION_NO_STOPPING;
ride->race_winner = SPRITE_INDEX_NULL;
ride->current_issues = 0;
ride->last_issue_time = 0;
ride_get_measurement(ride, nullptr);
ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAIN | RIDE_INVALIDATE_RIDE_LIST;
window_invalidate_by_number(WC_RIDE, _rideIndex);
break;
}
case RIDE_STATUS_TESTING:
case RIDE_STATUS_OPEN:
{
@ -147,6 +170,12 @@ public:
return res;
}
if (ride->status == RIDE_STATUS_SIMULATING)
{
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
}
// Fix #3183: Make sure we close the construction window so the ride finishes any editing code before opening
// otherwise vehicles get added to the ride incorrectly (such as to a ghost station)
rct_window* constructionWindow = window_find_by_number(WC_RIDE_CONSTRUCTION, _rideIndex);
@ -157,7 +186,7 @@ public:
if (_status == RIDE_STATUS_TESTING)
{
if (!ride_is_valid_for_test(ride, _status == RIDE_STATUS_OPEN, 1))
if (!ride_is_valid_for_test(ride, _status, 1))
{
res->Error = GA_ERROR::UNKNOWN;
res->ErrorMessage = gGameCommandErrorText;

View File

@ -92,7 +92,7 @@ public:
return std::make_unique<GameActionResult>(GA_ERROR::BROKEN, errTitle, STR_HAS_BROKEN_DOWN_AND_REQUIRES_FIXING);
}
if (ride->status != RIDE_STATUS_CLOSED)
if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING)
{
return std::make_unique<GameActionResult>(GA_ERROR::NOT_CLOSED, errTitle, STR_MUST_BE_CLOSED_FIRST);
}

View File

@ -3959,6 +3959,10 @@ enum
STR_DESYNC_REPORT = 6318,
STR_SIMULATING = 6323,
STR_SIMULATE_RIDE = 6324,
STR_SIMULATE_RIDE_TIP = 6325,
// Have to include resource strings (from scenarios and objects) for the time being now that language is partially working
STR_COUNT = 32768
};

View File

@ -182,7 +182,7 @@ extern LocationXY8 gClipSelectionA;
extern LocationXY8 gClipSelectionB;
/** rct2: 0x00993CC4. The white ghost that indicates not-yet-built elements. */
#define CONSTRUCTION_MARKER (COLOUR_DARK_GREEN << 19 | COLOUR_GREY << 24 | IMAGE_TYPE_REMAP);
#define CONSTRUCTION_MARKER (COLOUR_DARK_GREEN << 19 | COLOUR_GREY << 24 | IMAGE_TYPE_REMAP)
extern bool gShowDirtyVisuals;
extern bool gPaintBoundingBoxes;
extern bool gPaintBlockedTiles;

View File

@ -5404,7 +5404,7 @@ void Guest::UpdateQueuing()
return;
}
Ride* ride = get_ride(current_ride);
if (ride->status == RIDE_STATUS_CLOSED || ride->status == RIDE_STATUS_TESTING)
if (ride->status != RIDE_STATUS_OPEN)
{
RemoveFromQueue();
SetState(PEEP_STATE_1);

View File

@ -897,7 +897,12 @@ void ride_get_status(const Ride* ride, rct_string_id* formatSecondary, int32_t*
return;
}
if (ride->status == RIDE_STATUS_TESTING)
else if (ride->status == RIDE_STATUS_SIMULATING)
{
*formatSecondary = STR_SIMULATING;
return;
}
else if (ride->status == RIDE_STATUS_TESTING)
{
*formatSecondary = STR_TEST_RUN;
return;
@ -1024,7 +1029,7 @@ static int32_t ride_check_if_construction_allowed(Ride* ride)
return 0;
}
if (ride->status != RIDE_STATUS_CLOSED)
if (ride->status != RIDE_STATUS_CLOSED && ride->status != RIDE_STATUS_SIMULATING)
{
set_format_arg(6, rct_string_id, ride->name);
set_format_arg(8, uint32_t, ride->name_arguments);
@ -1985,7 +1990,10 @@ int32_t ride_modify(CoordsXYE* input)
}
// Stop the ride again to clear all vehicles and peeps (compatible with network games)
ride_set_status(ride, RIDE_STATUS_CLOSED);
if (ride->status != RIDE_STATUS_SIMULATING)
{
ride_set_status(ride, RIDE_STATUS_CLOSED);
}
// Check if element is a station entrance or exit
if (tileElement.element->GetType() == TILE_ELEMENT_TYPE_ENTRANCE)
@ -2192,24 +2200,10 @@ void Ride::Update()
ride_inspection_update(this);
if (status == RIDE_STATUS_TESTING && gConfigGeneral.no_test_crashes)
// If ride is simulating but crashed, reset the vehicles
if (status == RIDE_STATUS_SIMULATING && (lifecycle_flags & RIDE_LIFECYCLE_CRASHED))
{
for (int32_t i = 0; i < num_vehicles; i++)
{
uint16_t spriteIndex = vehicles[i];
if (spriteIndex == SPRITE_INDEX_NULL)
continue;
rct_vehicle* vehicle = GET_VEHICLE(spriteIndex);
if (vehicle->status == VEHICLE_STATUS_CRASHED || vehicle->status == VEHICLE_STATUS_CRASHING)
{
ride_set_status(this, RIDE_STATUS_CLOSED);
ride_set_status(this, RIDE_STATUS_CLOSED);
ride_set_status(this, RIDE_STATUS_TESTING);
break;
}
}
ride_set_status(this, RIDE_STATUS_SIMULATING);
}
}
@ -5396,7 +5390,7 @@ static TileElement* loc_6B4F6B(ride_id_t rideIndex, int32_t x, int32_t y)
return nullptr;
}
int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isApplying)
int32_t ride_is_valid_for_test(Ride* ride, int32_t status, int32_t isApplying)
{
int32_t stationIndex;
CoordsXYE trackElement, problematicTrackElement = {};
@ -5407,7 +5401,10 @@ int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isAppl
return 0;
}
window_close_by_number(WC_RIDE_CONSTRUCTION, ride->id);
if (status != RIDE_STATUS_SIMULATING)
{
window_close_by_number(WC_RIDE_CONSTRUCTION, ride->id);
}
stationIndex = ride_mode_check_station_present(ride);
if (stationIndex == -1)
@ -5422,7 +5419,7 @@ int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isAppl
return 0;
}
if (goingToBeOpen && isApplying)
if (status == RIDE_STATUS_OPEN && isApplying)
{
sub_6B5952(ride);
ride->lifecycle_flags |= RIDE_LIFECYCLE_EVER_BEEN_OPENED;
@ -5443,7 +5440,7 @@ int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isAppl
|| ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED || ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED)
{
if (ride_find_track_gap(ride, &trackElement, &problematicTrackElement)
&& (!gConfigGeneral.test_unfinished_tracks || ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED
&& (status != RIDE_STATUS_SIMULATING || ride->mode == RIDE_MODE_CONTINUOUS_CIRCUIT_BLOCK_SECTIONED
|| ride->mode == RIDE_MODE_POWERED_LAUNCH_BLOCK_SECTIONED))
{
gGameCommandErrorText = STR_TRACK_IS_NOT_A_COMPLETE_CIRCUIT;

View File

@ -631,7 +631,8 @@ enum
{
RIDE_STATUS_CLOSED,
RIDE_STATUS_OPEN,
RIDE_STATUS_TESTING
RIDE_STATUS_TESTING,
RIDE_STATUS_SIMULATING,
};
enum
@ -1074,7 +1075,7 @@ rct_ride_measurement* ride_get_measurement(Ride* ride, rct_string_id* message);
void ride_breakdown_add_news_item(Ride* ride);
Peep* ride_find_closest_mechanic(Ride* ride, int32_t forInspection);
int32_t ride_is_valid_for_open(Ride* ride, int32_t goingToBeOpen, int32_t isApplying);
int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isApplying);
int32_t ride_is_valid_for_test(Ride* ride, int32_t status, int32_t isApplying);
int32_t ride_initialise_construction_window(Ride* ride);
void ride_construction_invalidate_current_track();
int32_t sub_6C683D(

View File

@ -3520,6 +3520,15 @@ static void vehicle_check_if_missing(rct_vehicle* vehicle)
news_item_add_to_queue(NEWS_ITEM_RIDE, STR_NEWS_VEHICLE_HAS_STALLED, vehicle->ride);
}
static void vehicle_simulate_crash(rct_vehicle* vehicle)
{
auto ride = get_ride(vehicle->ride);
if (ride != nullptr)
{
ride->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED;
}
}
/**
* Setup function for a vehicle colliding with
* another vehicle.
@ -3528,10 +3537,16 @@ static void vehicle_check_if_missing(rct_vehicle* vehicle)
*/
static void vehicle_update_collision_setup(rct_vehicle* vehicle)
{
auto ride = get_ride(vehicle->ride);
if (ride != nullptr && ride->status == RIDE_STATUS_SIMULATING)
{
vehicle_simulate_crash(vehicle);
return;
}
vehicle->status = VEHICLE_STATUS_CRASHED;
vehicle_invalidate_window(vehicle);
Ride* ride = get_ride(vehicle->ride);
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED))
{
auto frontVehicle = vehicle->GetHead();
@ -3603,6 +3618,13 @@ static constexpr const LocationXY16 stru_9A3AC4[] = {
*/
static void vehicle_update_crash_setup(rct_vehicle* vehicle)
{
auto ride = get_ride(vehicle->ride);
if (ride != nullptr && ride->status == RIDE_STATUS_SIMULATING)
{
vehicle_simulate_crash(vehicle);
return;
}
vehicle->status = VEHICLE_STATUS_CRASHING;
vehicle_invalidate_window(vehicle);
@ -5266,10 +5288,16 @@ static void vehicle_kill_all_passengers(rct_vehicle* vehicle)
static void vehicle_crash_on_land(rct_vehicle* vehicle)
{
auto ride = get_ride(vehicle->ride);
if (ride != nullptr && ride->status == RIDE_STATUS_SIMULATING)
{
vehicle_simulate_crash(vehicle);
return;
}
vehicle->status = VEHICLE_STATUS_CRASHED;
vehicle_invalidate_window(vehicle);
Ride* ride = get_ride(vehicle->ride);
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED))
{
auto frontVehicle = vehicle->GetHead();
@ -5320,10 +5348,16 @@ static void vehicle_crash_on_land(rct_vehicle* vehicle)
static void vehicle_crash_on_water(rct_vehicle* vehicle)
{
auto ride = get_ride(vehicle->ride);
if (ride != nullptr && ride->status == RIDE_STATUS_SIMULATING)
{
vehicle_simulate_crash(vehicle);
return;
}
vehicle->status = VEHICLE_STATUS_CRASHED;
vehicle_invalidate_window(vehicle);
Ride* ride = get_ride(vehicle->ride);
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED))
{
auto frontVehicle = vehicle->GetHead();

View File

@ -899,6 +899,13 @@ static void vehicle_sprite_paint(
}
vehicle_boundbox bb = VehicleBoundboxes[vehicleEntry->draw_order][ecx];
bool isGhost = false;
auto ride = get_ride(vehicle->ride);
if (ride != nullptr && ride->status == RIDE_STATUS_SIMULATING)
{
isGhost = true;
}
if (vehicleEntry->flags & VEHICLE_ENTRY_FLAG_SPINNING_ADDITIONAL_FRAMES)
{
baseImage_id += (vehicle->spin_sprite / 8) & 31;
@ -909,6 +916,12 @@ static void vehicle_sprite_paint(
}
int32_t image_id = baseImage_id | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24)
| IMAGE_TYPE_REMAP_2_PLUS;
if (isGhost)
{
image_id &= 0x7FFFF;
image_id |= CONSTRUCTION_MARKER;
}
paint_struct* ps = sub_98197C(
session, image_id, 0, 0, bb.length_x, bb.length_y, bb.length_z, z, bb.offset_x, bb.offset_y, bb.offset_z + z);
if (ps != nullptr)
@ -930,6 +943,13 @@ static void vehicle_sprite_paint(
{
image_id += (vehicleEntry->no_vehicle_images * vehicle->animation_frame);
}
if (isGhost)
{
image_id &= 0x7FFFF;
image_id |= CONSTRUCTION_MARKER;
}
sub_98199C(
session, image_id, 0, 0, bb.length_x, bb.length_y, bb.length_z, z, bb.offset_x, bb.offset_y,
bb.offset_z + z);