Merge pull request #9222 from IntelOrca/implement/485-ghost-train-simulation

Implement #485: ghost train simulation
This commit is contained in:
Aaron van Geffen 2019-06-04 20:24:16 +02:00 committed by GitHub
commit f1ac847fce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 501 additions and 278 deletions

View File

@ -3770,6 +3770,10 @@ 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
STR_6326 :Can't simulate {POP16}{POP16}{POP16}{STRINGID}...
#############
# 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

View File

@ -430,6 +430,23 @@
{
"path": "icons/multiplayer_desync.png"
},
{
"path": "icons/simulate.png",
"x_offset": 2,
"y_offset": 2
},
{
"path": "icons/rct1_simulate_off.png"
},
{
"path": "icons/rct1_simulate_off_pressed.png"
},
{
"path": "icons/rct1_simulate_on.png"
},
{
"path": "icons/rct1_simulate_on_pressed.png"
},
{
"path": "font/latin/ae-uc-small.png",
"y_offset": 0,

View File

@ -173,7 +173,6 @@ enum WINDOW_OPTIONS_WIDGET_IDX {
// Advanced
WIDX_DEBUGGING_TOOLS = WIDX_PAGE_START,
WIDX_TEST_UNFINISHED_TRACKS,
WIDX_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM,
WIDX_SAVE_PLUGIN_DATA_CHECKBOX,
WIDX_STAY_CONNECTED_AFTER_DESYNC,
@ -359,17 +358,16 @@ static rct_widget window_options_misc_widgets[] = {
static rct_widget window_options_advanced_widgets[] = {
MAIN_OPTIONS_WIDGETS,
{ WWT_CHECKBOX, 2, 10, 299, 54, 65, STR_ENABLE_DEBUGGING_TOOLS, STR_ENABLE_DEBUGGING_TOOLS_TIP }, // Enable debugging tools
{ WWT_CHECKBOX, 2, 10, 299, 69, 80, STR_TEST_UNFINISHED_TRACKS, STR_TEST_UNFINISHED_TRACKS_TIP }, // Test unfinished tracks
{ WWT_CHECKBOX, 2, 10, 299, 84, 95, STR_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM, STR_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM_TIP }, // Allow loading with incorrect checksum
{ WWT_CHECKBOX, 2, 10, 299, 99, 110, STR_SAVE_PLUGIN_DATA, STR_SAVE_PLUGIN_DATA_TIP }, // Export plug-in objects with saved games
{ WWT_CHECKBOX, 2, 10, 299, 114, 125, STR_STAY_CONNECTED_AFTER_DESYNC, STR_STAY_CONNECTED_AFTER_DESYNC_TIP }, // Do not disconnect after the client desynchronises with the server
{ WWT_CHECKBOX, 1, 10, 299, 129, 140, STR_ALWAYS_NATIVE_LOADSAVE, STR_ALWAYS_NATIVE_LOADSAVE_TIP }, // Use native load/save window
{ WWT_DROPDOWN, 1, 165, 299, 145, 157, STR_NONE, STR_NONE }, // Autosave dropdown
{ WWT_BUTTON, 1, 288, 298, 146, 156, STR_DROPDOWN_GLYPH, STR_AUTOSAVE_FREQUENCY_TIP }, // Autosave dropdown button
SPINNER_WIDGETS (1, 165, 299, 165, 176, STR_NONE, STR_AUTOSAVE_AMOUNT_TIP ), // Autosave amount spinner
{ WWT_LABEL, 1, 23, 298, 184, 195, STR_PATH_TO_RCT1, STR_PATH_TO_RCT1_TIP }, // RCT 1 path text
{ WWT_BUTTON, 1, 24, 289, 199, 212, STR_NONE, STR_STRING_TOOLTIP }, // RCT 1 path button
{ WWT_BUTTON, 1, 289, 299, 199, 212, STR_CLOSE_X, STR_PATH_TO_RCT1_CLEAR_TIP }, // RCT 1 path clear button
{ WWT_CHECKBOX, 2, 10, 299, 69, 80, STR_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM, STR_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM_TIP }, // Allow loading with incorrect checksum
{ WWT_CHECKBOX, 2, 10, 299, 84, 95, STR_SAVE_PLUGIN_DATA, STR_SAVE_PLUGIN_DATA_TIP }, // Export plug-in objects with saved games
{ WWT_CHECKBOX, 2, 10, 299, 99, 110, STR_STAY_CONNECTED_AFTER_DESYNC, STR_STAY_CONNECTED_AFTER_DESYNC_TIP }, // Do not disconnect after the client desynchronises with the server
{ WWT_CHECKBOX, 1, 10, 299, 114, 125, STR_ALWAYS_NATIVE_LOADSAVE, STR_ALWAYS_NATIVE_LOADSAVE_TIP }, // Use native load/save window
{ WWT_DROPDOWN, 1, 165, 299, 130, 142, STR_NONE, STR_NONE }, // Autosave dropdown
{ WWT_BUTTON, 1, 288, 298, 131, 141, STR_DROPDOWN_GLYPH, STR_AUTOSAVE_FREQUENCY_TIP }, // Autosave dropdown button
SPINNER_WIDGETS (1, 165, 299, 150, 161, STR_NONE, STR_AUTOSAVE_AMOUNT_TIP ), // Autosave amount spinner
{ WWT_LABEL, 1, 23, 298, 169, 180, STR_PATH_TO_RCT1, STR_PATH_TO_RCT1_TIP }, // RCT 1 path text
{ WWT_BUTTON, 1, 24, 289, 184, 197, STR_NONE, STR_STRING_TOOLTIP }, // RCT 1 path button
{ WWT_BUTTON, 1, 289, 299, 184, 197, STR_CLOSE_X, STR_PATH_TO_RCT1_CLEAR_TIP }, // RCT 1 path clear button
{ WIDGETS_END },
};
@ -601,7 +599,6 @@ static uint64_t window_options_page_enabled_widgets[] = {
MAIN_OPTIONS_ENABLED_WIDGETS |
(1 << WIDX_DEBUGGING_TOOLS) |
(1 << WIDX_TEST_UNFINISHED_TRACKS) |
(1 << WIDX_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM) |
(1 << WIDX_SAVE_PLUGIN_DATA_CHECKBOX) |
(1 << WIDX_STAY_CONNECTED_AFTER_DESYNC) |
@ -924,11 +921,6 @@ static void window_options_mouseup(rct_window* w, rct_widgetindex widgetIndex)
config_save_default();
gfx_invalidate_screen();
break;
case WIDX_TEST_UNFINISHED_TRACKS:
gConfigGeneral.test_unfinished_tracks ^= 1;
config_save_default();
window_invalidate(w);
break;
case WIDX_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM:
gConfigGeneral.allow_loading_with_incorrect_checksum = !gConfigGeneral
.allow_loading_with_incorrect_checksum;
@ -1926,7 +1918,6 @@ static void window_options_invalidate(rct_window* w)
case WINDOW_OPTIONS_PAGE_ADVANCED:
widget_set_checkbox_value(w, WIDX_DEBUGGING_TOOLS, gConfigGeneral.debugging_tools);
widget_set_checkbox_value(w, WIDX_TEST_UNFINISHED_TRACKS, gConfigGeneral.test_unfinished_tracks);
widget_set_checkbox_value(
w, WIDX_ALLOW_LOADING_WITH_INCORRECT_CHECKSUM, gConfigGeneral.allow_loading_with_incorrect_checksum);
widget_set_checkbox_value(w, WIDX_SAVE_PLUGIN_DATA_CHECKBOX, gConfigGeneral.save_plugin_data);

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;
@ -2055,12 +2062,32 @@ static void window_ride_main_mouseup(rct_window* w, rct_widgetindex widgetIndex)
*/
static void window_ride_main_resize(rct_window* w)
{
const int32_t offset = gCheatsAllowArbitraryRideTypeChanges ? 15 : 0;
w->flags |= WF_RESIZABLE;
int32_t minHeight = 180 + offset;
int32_t minHeight = 180;
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);
{
minHeight += 20 + RCT1_LIGHT_OFFSET;
auto ride = get_ride(w->number);
if (ride != nullptr)
{
#ifdef __SIMULATE_IN_RIDE_WINDOW__
if (ride->SupportsStatus(RIDE_STATUS_SIMULATING))
{
minHeight += 14;
}
#endif
if (ride->SupportsStatus(RIDE_STATUS_TESTING))
{
minHeight += 14;
}
}
}
if (gCheatsAllowArbitraryRideTypeChanges)
{
minHeight += 15;
}
w->flags |= WF_RESIZABLE;
window_set_resize(w, 316, minHeight, 500, 450);
window_ride_init_viewport(w);
}
@ -2123,78 +2150,80 @@ static void window_ride_show_view_dropdown(rct_window* w, rct_widget* widget)
dropdown_set_checked(w->ride.view, true);
}
/**
*
* rct2: 0x006AF64C
*/
static void window_ride_show_open_dropdown(rct_window* w, rct_widget* widget)
static uint8_t window_ride_get_next_default_status(const Ride* ride)
{
Ride* ride;
int32_t numItems, highlightedIndex = 0, checkedIndex;
ride = get_ride(w->number);
numItems = 0;
gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[numItems] = STR_CLOSE_RIDE;
numItems++;
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE))
{
gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[numItems] = STR_TEST_RIDE;
numItems++;
}
gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[numItems] = STR_OPEN_RIDE;
numItems++;
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;
switch (ride->status)
{
default:
case RIDE_STATUS_CLOSED:
highlightedIndex = 0;
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_CRASHED)
|| (ride->lifecycle_flags & RIDE_LIFECYCLE_HAS_STALLED_VEHICLE))
break;
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;
break;
{
return RIDE_STATUS_CLOSED;
}
else if (ride->SupportsStatus(RIDE_STATUS_TESTING) && !(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED))
{
return RIDE_STATUS_TESTING;
}
else
{
return RIDE_STATUS_OPEN;
}
case RIDE_STATUS_SIMULATING:
return RIDE_STATUS_TESTING;
case RIDE_STATUS_TESTING:
highlightedIndex = 2;
if (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)
break;
highlightedIndex = 0;
break;
return (ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED) ? RIDE_STATUS_OPEN : RIDE_STATUS_CLOSED;
case RIDE_STATUS_OPEN:
highlightedIndex = 0;
break;
return RIDE_STATUS_CLOSED;
}
}
if (checkedIndex != RIDE_STATUS_CLOSED)
checkedIndex = 3 - checkedIndex;
struct RideStatusDropdownInfo
{
struct Ride* Ride{};
uint8_t CurrentStatus{};
uint8_t DefaultStatus{};
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE))
int32_t NumItems{};
int32_t CheckedIndex = -1;
int32_t DefaultIndex = -1;
};
static void window_ride_set_dropdown(RideStatusDropdownInfo& info, uint8_t status, rct_string_id text)
{
if (info.Ride->SupportsStatus(status))
{
if (checkedIndex != 0)
checkedIndex--;
if (highlightedIndex != 0)
highlightedIndex--;
auto index = info.NumItems;
gDropdownItemsFormat[index] = STR_DROPDOWN_MENU_LABEL;
gDropdownItemsArgs[index] = text;
if (info.CurrentStatus == status)
{
info.CheckedIndex = index;
}
if (info.DefaultStatus == status)
{
info.DefaultIndex = index;
}
info.NumItems++;
}
}
dropdown_set_checked(checkedIndex, true);
gDropdownDefaultIndex = highlightedIndex;
static void window_ride_show_open_dropdown(rct_window* w, rct_widget* widget)
{
RideStatusDropdownInfo info;
info.Ride = get_ride(w->number);
info.CurrentStatus = info.Ride->status;
info.DefaultStatus = window_ride_get_next_default_status(info.Ride);
window_ride_set_dropdown(info, RIDE_STATUS_CLOSED, STR_CLOSE_RIDE);
#ifdef __SIMULATE_IN_RIDE_WINDOW__
window_ride_set_dropdown(info, RIDE_STATUS_SIMULATING, STR_SIMULATE_RIDE);
#endif
window_ride_set_dropdown(info, RIDE_STATUS_TESTING, STR_TEST_RIDE);
window_ride_set_dropdown(info, RIDE_STATUS_OPEN, STR_OPEN_RIDE);
window_dropdown_show_text(
w->x + widget->left, w->y + widget->top, widget->bottom - widget->top + 1, w->colours[1], 0, info.NumItems);
dropdown_set_checked(info.CheckedIndex, true);
gDropdownDefaultIndex = info.DefaultIndex;
}
static void populate_ride_type_dropdown()
@ -2383,23 +2412,25 @@ static void window_ride_main_mousedown(rct_window* w, rct_widgetindex widgetInde
*/
static void window_ride_main_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
{
Ride* ride;
int32_t status = 0;
switch (widgetIndex)
{
case WIDX_VIEW_DROPDOWN:
if (dropdownIndex == -1)
{
dropdownIndex = w->ride.view;
ride = get_ride(w->number);
dropdownIndex++;
if (dropdownIndex != 0 && dropdownIndex <= ride->num_vehicles
&& !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK))
dropdownIndex = ride->num_vehicles + 1;
if (dropdownIndex >= gDropdownNumItems)
dropdownIndex = 0;
dropdownIndex = w->ride.view + 1;
auto ride = get_ride(w->number);
if (ride != nullptr)
{
if (dropdownIndex != 0 && dropdownIndex <= ride->num_vehicles
&& !(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK))
{
dropdownIndex = ride->num_vehicles + 1;
}
if (dropdownIndex >= gDropdownNumItems)
{
dropdownIndex = 0;
}
}
}
w->ride.view = dropdownIndex;
@ -2407,27 +2438,37 @@ static void window_ride_main_dropdown(rct_window* w, rct_widgetindex widgetIndex
window_invalidate(w);
break;
case WIDX_OPEN:
if (dropdownIndex == -1)
dropdownIndex = gDropdownHighlightedIndex;
ride = get_ride(w->number);
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) && dropdownIndex != 0)
dropdownIndex++;
switch (dropdownIndex)
{
auto ride = get_ride(w->number);
if (ride != nullptr)
{
case 0:
status = RIDE_STATUS_CLOSED;
break;
case 1:
status = RIDE_STATUS_TESTING;
break;
case 2:
status = RIDE_STATUS_OPEN;
break;
auto status = RIDE_STATUS_CLOSED;
if (dropdownIndex < 0)
{
dropdownIndex = gDropdownHighlightedIndex;
}
if (dropdownIndex < (int32_t)std::size(gDropdownItemsArgs))
{
switch (gDropdownItemsArgs[dropdownIndex])
{
case STR_CLOSE_RIDE:
status = RIDE_STATUS_CLOSED;
break;
case STR_SIMULATE_RIDE:
status = RIDE_STATUS_SIMULATING;
break;
case STR_TEST_RIDE:
status = RIDE_STATUS_TESTING;
break;
case STR_OPEN_RIDE:
status = RIDE_STATUS_OPEN;
break;
}
}
ride_set_status(ride, status);
}
ride_set_status(ride, status);
break;
}
case WIDX_RIDE_TYPE_DROPDOWN:
if (dropdownIndex != -1 && dropdownIndex < RIDE_TYPE_COUNT)
{
@ -2533,13 +2574,26 @@ static void window_ride_main_invalidate(rct_window* w)
SPR_CLOSED,
SPR_OPEN,
SPR_TESTING,
SPR_G2_SIMULATE,
};
window_ride_main_widgets[WIDX_OPEN].image = spriteIds[ride->status];
#ifdef __SIMULATE_IN_RIDE_WINDOW__
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_SIMULATE_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);
#else
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);
auto baseSprite = ride->status == RIDE_STATUS_SIMULATING ? SPR_G2_RCT1_SIMULATE_BUTTON_0 : SPR_G2_RCT1_TEST_BUTTON_0;
window_ride_main_widgets[WIDX_TEST_LIGHT].image = baseSprite
+ (ride->status == RIDE_STATUS_TESTING || ride->status == RIDE_STATUS_SIMULATING) * 2
+ widget_is_pressed(w, WIDX_TEST_LIGHT);
#endif
window_ride_main_widgets[WIDX_OPEN_LIGHT].image = SPR_G2_RCT1_OPEN_BUTTON_0 + (ride->status == RIDE_STATUS_OPEN) * 2
+ widget_is_pressed(w, WIDX_OPEN_LIGHT);
@ -2580,11 +2634,21 @@ 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_TEST_LIGHT].type
= (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? WWT_EMPTY : WWT_IMGBTN);
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WWT_EMPTY;
#ifdef __SIMULATE_IN_RIDE_WINDOW__
if (ride->SupportsStatus(RIDE_STATUS_SIMULATING))
window_ride_main_widgets[WIDX_SIMULATE_LIGHT].type = WWT_IMGBTN;
#endif
window_ride_main_widgets[WIDX_TEST_LIGHT].type = ride->SupportsStatus(RIDE_STATUS_TESTING) ? WWT_IMGBTN : WWT_EMPTY;
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;
@ -2594,15 +2658,12 @@ static void window_ride_main_invalidate(rct_window* w)
window_ride_main_widgets[WIDX_OPEN_LIGHT].top = height;
window_ride_main_widgets[WIDX_OPEN_LIGHT].bottom = height + 13;
height += 14 - 24 + RCT1_LIGHT_OFFSET;
w->min_height = 200 + RCT1_LIGHT_OFFSET - (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE) ? 14 : 0);
if (w->height < w->min_height)
window_event_resize_call(w);
}
else
{
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

@ -79,6 +79,7 @@ enum {
WIDX_SEAT_ROTATION_ANGLE_SPINNER,
WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP,
WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN,
WIDX_SIMULATE,
};
validate_global_widx(WC_RIDE_CONSTRUCTION, WIDX_CONSTRUCT);
@ -113,8 +114,8 @@ static rct_widget window_ride_construction_widgets[] = {
{ WWT_IMGBTN, 1, 3, 162, 164, 333, 0xFFFFFFFF, STR_RIDE_CONSTRUCTION_CONSTRUCT_SELECTED_SECTION_TIP },
{ WWT_FLATBTN, 1, 60, 105, 338, 361, SPR_DEMOLISH_CURRENT_SECTION, STR_RIDE_CONSTRUCTION_REMOVE_HIGHLIGHTED_SECTION_TIP },
{ WWT_FLATBTN, 1, 50, 71, 29, 52, SPR_RIDE_CONSTRUCTION_LEFT_CURVE_LARGE, STR_RIDE_CONSTRUCTION_LEFT_CURVE_LARGE_TIP },
{ WWT_FLATBTN, 1, 20, 43, 338, 361, SPR_PREVIOUS, STR_RIDE_CONSTRUCTION_MOVE_TO_PREVIOUS_SECTION_TIP },
{ WWT_FLATBTN, 1, 122, 145, 338, 361, SPR_NEXT, STR_RIDE_CONSTRUCTION_MOVE_TO_NEXT_SECTION_TIP },
{ WWT_FLATBTN, 1, 30, 53, 338, 361, SPR_PREVIOUS, STR_RIDE_CONSTRUCTION_MOVE_TO_PREVIOUS_SECTION_TIP },
{ WWT_FLATBTN, 1, 112, 135, 338, 361, SPR_NEXT, STR_RIDE_CONSTRUCTION_MOVE_TO_NEXT_SECTION_TIP },
{ WWT_GROUPBOX, 0, 3, 162, 362, 389, 0xFFFFFFFF, STR_NONE },
{ WWT_BUTTON, 1, 9, 78, 372, 383, STR_RIDE_CONSTRUCTION_ENTRANCE, STR_RIDE_CONSTRUCTION_ENTRANCE_TIP },
{ WWT_BUTTON, 1, 87, 156, 372, 383, STR_RIDE_CONSTRUCTION_EXIT, STR_RIDE_CONSTRUCTION_EXIT_TIP },
@ -124,6 +125,7 @@ static rct_widget window_ride_construction_widgets[] = {
{ WWT_FLATBTN, 1, 123, 146, 132, 155, SPR_RIDE_CONSTRUCTION_O_SHAPED_TRACK, STR_RIDE_CONSTRUCTION_O_SHAPED_ENCLOSED_TRACK_TIP },
{ WWT_GROUPBOX, 0, 96, 162, 120, 160, STR_RIDE_CONSTRUCTION_SEAT_ROT, STR_NONE },
SPINNER_WIDGETS (1, 101, 158, 138, 149, 0, STR_RIDE_CONSTRUCTION_SELECT_SEAT_ROTATION_ANGLE_TIP),
{ WWT_FLATBTN, 1, 139, 162, 338, 361, SPR_G2_SIMULATE, STR_SIMULATE_RIDE_TIP },
{ WIDGETS_END }
};
@ -540,8 +542,8 @@ rct_window* window_ride_construction_open()
| (1ULL << WIDX_SLOPE_DOWN) | (1ULL << WIDX_LEVEL) | (1ULL << WIDX_SLOPE_UP) | (1ULL << WIDX_SLOPE_UP_STEEP)
| (1ULL << WIDX_CHAIN_LIFT) | (1ULL << WIDX_BANK_LEFT) | (1ULL << WIDX_BANK_STRAIGHT) | (1ULL << WIDX_BANK_RIGHT)
| (1ULL << WIDX_CONSTRUCT) | (1ULL << WIDX_DEMOLISH) | (1ULL << WIDX_LEFT_CURVE_LARGE) | (1ULL << WIDX_PREVIOUS_SECTION)
| (1ULL << WIDX_NEXT_SECTION) | (1ULL << WIDX_ENTRANCE) | (1ULL << WIDX_EXIT) | (1ULL << WIDX_RIGHT_CURVE_LARGE)
| (1ULL << WIDX_ROTATE) | (1ULL << WIDX_U_TRACK) | (1ULL << WIDX_O_TRACK)
| (1ULL << WIDX_NEXT_SECTION) | (1ULL << WIDX_SIMULATE) | (1ULL << WIDX_ENTRANCE) | (1ULL << WIDX_EXIT)
| (1ULL << WIDX_RIGHT_CURVE_LARGE) | (1ULL << WIDX_ROTATE) | (1ULL << WIDX_U_TRACK) | (1ULL << WIDX_O_TRACK)
| (1ULL << WIDX_SEAT_ROTATION_ANGLE_SPINNER_UP) | (1ULL << WIDX_SEAT_ROTATION_ANGLE_SPINNER_DOWN);
window_init_scroll_widgets(w);
@ -674,6 +676,16 @@ static void window_ride_construction_mouseup(rct_window* w, rct_widgetindex widg
case WIDX_EXIT:
window_ride_construction_exit_click(w);
break;
case WIDX_SIMULATE:
{
auto ride = get_ride(_currentRideIndex);
if (ride != nullptr)
{
auto status = ride->status == RIDE_STATUS_SIMULATING ? RIDE_STATUS_CLOSED : RIDE_STATUS_SIMULATING;
ride_set_status(ride, status);
}
break;
}
}
}
@ -2029,7 +2041,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;
@ -2255,6 +2267,22 @@ static void window_ride_construction_invalidate(rct_window* w)
window_ride_construction_widgets[WIDX_SEAT_ROTATION_ANGLE_SPINNER].text = RideConstructionSeatAngleRotationStrings
[_currentSeatRotationAngle];
// Simulate button
auto& simulateWidget = w->widgets[WIDX_SIMULATE];
simulateWidget.type = WWT_EMPTY;
if (ride->SupportsStatus(RIDE_STATUS_SIMULATING))
{
simulateWidget.type = WWT_FLATBTN;
if (ride->status == RIDE_STATUS_SIMULATING)
{
w->pressed_widgets |= (1ULL << WIDX_SIMULATE);
}
else
{
w->pressed_widgets &= ~(1ULL << WIDX_SIMULATE);
}
}
// Set window title arguments
set_format_arg(4, rct_string_id, ride->name);
set_format_arg(6, uint32_t, ride->name_arguments);

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);
}
@ -89,9 +89,6 @@ public:
return MakeResult(GA_ERROR::DISALLOWED, errorTitle, STR_NOT_ALLOWED_TO_MODIFY_STATION);
}
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
const auto location = _isExit ? ride_get_exit_location(ride, _stationNum)
: ride_get_entrance_location(ride, _stationNum);
@ -156,8 +153,11 @@ public:
return MakeResult(GA_ERROR::INVALID_PARAMETERS, errorTitle);
}
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
}
const auto location = _isExit ? ride_get_exit_location(ride, _stationNum)
: ride_get_entrance_location(ride, _stationNum);

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);
}
@ -123,9 +123,12 @@ public:
return std::make_unique<GameActionResult>(GA_ERROR::INVALID_PARAMETERS, STR_NONE);
}
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
invalidate_test_results(ride);
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
ride_clear_for_construction(ride);
ride_remove_peeps(ride);
invalidate_test_results(ride);
}
bool found = false;
TileElement* tileElement = map_get_first_element_at(_loc.x / 32, _loc.y / 32);

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

@ -25,6 +25,7 @@ static rct_string_id _StatusErrorTitles[] = {
STR_CANT_CLOSE,
STR_CANT_OPEN,
STR_CANT_TEST,
STR_CANT_SIMULATE,
};
DEFINE_GAME_ACTION(RideSetStatusAction, GAME_COMMAND_SET_RIDE_STATUS, GameActionResult)
@ -73,9 +74,16 @@ public:
if (_status != ride->status)
{
if (_status == RIDE_STATUS_TESTING)
if (_status == RIDE_STATUS_SIMULATING && (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN))
{
if (!ride_is_valid_for_test(ride, _status == RIDE_STATUS_OPEN, 0))
// Simulating will force clear the track, so make sure player can't cheat around a break down
res->Error = GA_ERROR::DISALLOWED;
res->ErrorMessage = STR_HAS_BROKEN_DOWN_AND_REQUIRES_FIXING;
return res;
}
else if (_status == RIDE_STATUS_TESTING || _status == RIDE_STATUS_SIMULATING)
{
if (!ride_is_valid_for_test(ride, _status, false))
{
res->Error = GA_ERROR::UNKNOWN;
res->ErrorMessage = gGameCommandErrorText;
@ -84,7 +92,7 @@ public:
}
else if (_status == RIDE_STATUS_OPEN)
{
if (!ride_is_valid_for_open(ride, _status == RIDE_STATUS_OPEN, 0))
if (!ride_is_valid_for_open(ride, _status == RIDE_STATUS_OPEN, false))
{
res->Error = GA_ERROR::UNKNOWN;
res->ErrorMessage = gGameCommandErrorText;
@ -123,7 +131,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 +147,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, true))
{
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 +178,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,14 +194,14 @@ 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, true))
{
res->Error = GA_ERROR::UNKNOWN;
res->ErrorMessage = gGameCommandErrorText;
return res;
}
}
else if (!ride_is_valid_for_open(ride, _status == RIDE_STATUS_OPEN, 1))
else if (!ride_is_valid_for_open(ride, _status == RIDE_STATUS_OPEN, true))
{
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

@ -170,11 +170,6 @@ namespace Config
model->use_vsync = reader->GetBoolean("use_vsync", true);
model->virtual_floor_style = reader->GetEnum<int32_t>(
"virtual_floor_style", VIRTUAL_FLOOR_STYLE_GLASSY, Enum_VirtualFloorStyle);
// Default config setting is false until ghost trains are implemented #4540
model->test_unfinished_tracks = reader->GetBoolean("test_unfinished_tracks", false);
model->no_test_crashes = reader->GetBoolean("no_test_crashes", false);
model->date_format = reader->GetEnum<int32_t>("date_format", platform_get_locale_date_format(), Enum_DateFormat);
model->auto_staff_placement = reader->GetBoolean("auto_staff", true);
model->handymen_mow_default = reader->GetBoolean("handymen_mow_default", false);
@ -250,8 +245,6 @@ namespace Config
writer->WriteEnum<int32_t>("drawing_engine", model->drawing_engine, Enum_DrawingEngine);
writer->WriteBoolean("uncap_fps", model->uncap_fps);
writer->WriteBoolean("use_vsync", model->use_vsync);
writer->WriteBoolean("test_unfinished_tracks", model->test_unfinished_tracks);
writer->WriteBoolean("no_test_crashes", model->no_test_crashes);
writer->WriteEnum<int32_t>("date_format", model->date_format, Enum_DateFormat);
writer->WriteBoolean("auto_staff", model->auto_staff_placement);
writer->WriteBoolean("handymen_mow_default", model->handymen_mow_default);

View File

@ -70,8 +70,6 @@ struct GeneralConfiguration
int32_t window_snap_proximity;
bool allow_loading_with_incorrect_checksum;
bool save_plugin_data;
bool test_unfinished_tracks;
bool no_test_crashes;
bool debugging_tools;
int32_t autosave_frequency;
int32_t autosave_amount;

View File

@ -650,14 +650,6 @@ static int32_t cc_get(InteractiveConsole& console, const arguments_t& argv)
{
console.WriteFormatLine("console_small_font %d", gConfigInterface.console_small_font);
}
else if (argv[0] == "test_unfinished_tracks")
{
console.WriteFormatLine("test_unfinished_tracks %d", gConfigGeneral.test_unfinished_tracks);
}
else if (argv[0] == "no_test_crashes")
{
console.WriteFormatLine("no_test_crashes %d", gConfigGeneral.no_test_crashes);
}
else if (argv[0] == "location")
{
rct_window* w = window_get_main();
@ -916,18 +908,6 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv)
config_save_default();
console.Execute("get console_small_font");
}
else if (argv[0] == "test_unfinished_tracks" && invalidArguments(&invalidArgs, int_valid[0]))
{
gConfigGeneral.test_unfinished_tracks = (int_val[0] != 0);
config_save_default();
console.Execute("get test_unfinished_tracks");
}
else if (argv[0] == "no_test_crashes" && invalidArguments(&invalidArgs, int_valid[0]))
{
gConfigGeneral.no_test_crashes = (int_val[0] != 0);
config_save_default();
console.Execute("get no_test_crashes");
}
else if (argv[0] == "location" && invalidArguments(&invalidArgs, int_valid[0] && int_valid[1]))
{
rct_window* w = window_get_main();
@ -1673,8 +1653,6 @@ static constexpr const utf8* console_variable_table[] = {
"climate",
"game_speed",
"console_small_font",
"test_unfinished_tracks",
"no_test_crashes",
"location",
"window_scale",
"window_limit",

View File

@ -2741,8 +2741,6 @@ enum
STR_LOCALE_DECIMAL_POINT = 5152,
STR_EDIT_THEMES_BUTTON = 5153,
STR_HARDWARE_DISPLAY = 5154, // Unused
STR_TEST_UNFINISHED_TRACKS = 5155,
STR_TEST_UNFINISHED_TRACKS_TIP = 5156,
STR_CHEAT_UNLOCK_PRICES = 5157,
STR_QUIT_TO_MENU = 5158,
STR_EXIT_OPENRCT2 = 5159,
@ -3959,6 +3957,11 @@ enum
STR_DESYNC_REPORT = 6318,
STR_SIMULATING = 6323,
STR_SIMULATE_RIDE = 6324,
STR_SIMULATE_RIDE_TIP = 6325,
STR_CANT_SIMULATE = 6326,
// 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;
@ -968,6 +973,23 @@ bool Ride::CanHaveMultipleCircuits() const
return true;
}
bool Ride::SupportsStatus(int32_t s) const
{
switch (s)
{
case RIDE_STATUS_CLOSED:
case RIDE_STATUS_OPEN:
return true;
case RIDE_STATUS_SIMULATING:
return (
!ride_type_has_flag(type, RIDE_TYPE_FLAG_NO_TEST_MODE) && ride_type_has_flag(type, RIDE_TYPE_FLAG_HAS_TRACK));
case RIDE_STATUS_TESTING:
return !ride_type_has_flag(type, RIDE_TYPE_FLAG_NO_TEST_MODE);
default:
return false;
}
}
#pragma region Initialisation functions
/**
@ -1024,7 +1046,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 +2007,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 +2217,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);
}
}
@ -2472,7 +2483,7 @@ static void ride_breakdown_update(Ride* ride)
if (ride->lifecycle_flags & (RIDE_LIFECYCLE_BREAKDOWN_PENDING | RIDE_LIFECYCLE_BROKEN_DOWN | RIDE_LIFECYCLE_CRASHED))
return;
if (ride->status == RIDE_STATUS_CLOSED)
if (ride->status == RIDE_STATUS_CLOSED || ride->status == RIDE_STATUS_SIMULATING)
return;
if (!ride->CanBreakDown())
@ -3141,7 +3152,7 @@ void ride_measurements_update()
continue;
Ride* ride = get_ride(measurement->ride_index);
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK))
if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK) || ride->status == RIDE_STATUS_SIMULATING)
continue;
if (measurement->flags & RIDE_MEASUREMENT_FLAG_RUNNING)
@ -5396,7 +5407,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, bool isApplying)
{
int32_t stationIndex;
CoordsXYE trackElement, problematicTrackElement = {};
@ -5407,7 +5418,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)
@ -5416,13 +5430,13 @@ int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isAppl
if (!ride_mode_check_valid_station_numbers(ride))
return 0;
if (!ride_check_for_entrance_exit(ride->id))
if (status != RIDE_STATUS_SIMULATING && !ride_check_for_entrance_exit(ride->id))
{
loc_6B51C0(ride);
return 0;
}
if (goingToBeOpen && isApplying)
if (status == RIDE_STATUS_OPEN && isApplying)
{
sub_6B5952(ride);
ride->lifecycle_flags |= RIDE_LIFECYCLE_EVER_BEEN_OPENED;
@ -5443,7 +5457,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;
@ -5532,7 +5546,7 @@ int32_t ride_is_valid_for_test(Ride* ride, int32_t goingToBeOpen, int32_t isAppl
*
* rct2: 0x006B4EEA
*/
int32_t ride_is_valid_for_open(Ride* ride, int32_t goingToBeOpen, int32_t isApplying)
int32_t ride_is_valid_for_open(Ride* ride, int32_t goingToBeOpen, bool isApplying)
{
int32_t stationIndex;
CoordsXYE trackElement, problematicTrackElement = {};

View File

@ -382,6 +382,7 @@ public:
bool IsPoweredLaunched() const;
bool IsBlockSectioned() const;
bool CanHaveMultipleCircuits() const;
bool SupportsStatus(int32_t s) const;
void StopGuestsQueuing();
@ -631,7 +632,8 @@ enum
{
RIDE_STATUS_CLOSED,
RIDE_STATUS_OPEN,
RIDE_STATUS_TESTING
RIDE_STATUS_TESTING,
RIDE_STATUS_SIMULATING,
};
enum
@ -1073,8 +1075,8 @@ void ride_measurements_update();
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_open(Ride* ride, int32_t goingToBeOpen, bool isApplying);
int32_t ride_is_valid_for_test(Ride* ride, int32_t status, bool isApplying);
int32_t ride_initialise_construction_window(Ride* ride);
void ride_construction_invalidate_current_track();
int32_t sub_6C683D(

View File

@ -2285,7 +2285,7 @@ static void vehicle_update_waiting_for_passengers(rct_vehicle* vehicle)
num_seats_on_train &= 0x7F;
if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_NO_TEST_MODE))
if (ride->SupportsStatus(RIDE_STATUS_TESTING))
{
if (vehicle->time_waiting < 20)
{
@ -3254,7 +3254,7 @@ static void vehicle_update_departing(rct_vehicle* vehicle)
vehicle_update_test_finish(vehicle);
}
}
else if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS))
else if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS) && !vehicle->IsGhost())
{
vehicle_test_reset(vehicle);
}
@ -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);
@ -4216,7 +4238,7 @@ static void vehicle_update_travelling_cable_lift(rct_vehicle* vehicle)
vehicle_update_test_finish(vehicle);
}
}
else if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS))
else if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS) && !vehicle->IsGhost())
{
vehicle_test_reset(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();
@ -10014,3 +10048,9 @@ const rct_vehicle* rct_vehicle::GetCar(size_t carIndex) const
}
return car;
}
bool rct_vehicle::IsGhost() const
{
auto r = get_ride(ride);
return r != nullptr && r->status == RIDE_STATUS_SIMULATING;
}

View File

@ -235,6 +235,7 @@ struct rct_vehicle : rct_sprite_common
const rct_vehicle* GetHead() const;
const rct_vehicle* GetCar(size_t carIndex) const;
void Invalidate();
bool IsGhost() const;
};
struct train_ref

View File

@ -909,6 +909,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 (vehicle->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 +936,13 @@ static void vehicle_sprite_paint(
{
image_id += (vehicleEntry->no_vehicle_images * vehicle->animation_frame);
}
if (vehicle->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);

View File

@ -196,10 +196,14 @@ void vehicle_visual_virginia_reel(
const vehicle_boundbox* bb = &_virginiaReelBoundbox[j];
image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour);
if (vehicle->IsGhost())
{
image_id = (image_id & 0x7FFFF) | CONSTRUCTION_MARKER;
}
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 (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0)
if (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0 && !vehicle->IsGhost())
{
uint8_t riding_peep_sprites[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
for (int32_t i = 0; i < vehicle->num_peeps; i++)

View File

@ -54,8 +54,11 @@ void vehicle_visual_observation_tower(
baseImage_id = (vehicle->animation_frame * 2) + vehicleEntry->base_image_id + 8;
}
image_id = baseImage_id | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24)
| IMAGE_TYPE_REMAP_2_PLUS;
image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_3(vehicle->colours.body_colour, vehicle->colours.trim_colour << 24);
if (vehicle->IsGhost())
{
image_id = (image_id & 0x7FFFF) | CONSTRUCTION_MARKER;
}
paint_struct* ps = sub_98197C(session, image_id, 0, 0, 2, 2, 41, z, -11, -11, z + 1);
if (ps != nullptr)
{

View File

@ -35,48 +35,50 @@ void vehicle_visual_launched_freefall(
paint_session* session, int32_t x, int32_t imageDirection, int32_t y, int32_t z, const rct_vehicle* vehicle,
const rct_ride_entry_vehicle* vehicleEntry)
{
int32_t image_id;
int32_t baseImage_id = vehicleEntry->base_image_id + ((vehicle->restraints_position / 64) * 2);
auto imageFlags = SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour);
if (vehicle->IsGhost())
{
imageFlags = CONSTRUCTION_MARKER;
}
// Draw back:
image_id = (baseImage_id + 2) | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour);
int32_t baseImage_id = vehicleEntry->base_image_id + ((vehicle->restraints_position / 64) * 2);
auto image_id = (baseImage_id + 2) | imageFlags;
sub_98197C(session, image_id, 0, 0, 2, 2, 41, z, -11, -11, z + 1);
// Draw front:
image_id = (baseImage_id + 1) | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour);
image_id = (baseImage_id + 1) | imageFlags;
sub_98197C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
// Draw peeps:
if (session->DPI.zoom_level < 2)
if (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0 && !vehicle->IsGhost())
{
if (vehicle->num_peeps > 0)
baseImage_id = vehicleEntry->base_image_id + 9;
if ((vehicle->restraints_position / 64) == 3)
{
baseImage_id = vehicleEntry->base_image_id + 9;
if ((vehicle->restraints_position / 64) == 3)
{
baseImage_id += 2; // Draw peeps sitting without transparent area between them for restraints
}
image_id = (baseImage_id + ((((imageDirection / 8) + 0) & 3) * 3))
| SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[0], vehicle->peep_tshirt_colours[1]);
baseImage_id += 2; // Draw peeps sitting without transparent area between them for restraints
}
auto directionOffset = imageDirection / 8;
image_id = (baseImage_id + (((directionOffset + 0) & 3) * 3))
| SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[0], vehicle->peep_tshirt_colours[1]);
sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
if (vehicle->num_peeps > 2)
{
image_id = (baseImage_id + (((directionOffset + 1) & 3) * 3))
| SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[2], vehicle->peep_tshirt_colours[3]);
sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
}
if (vehicle->num_peeps > 4)
{
image_id = (baseImage_id + (((directionOffset + 2) & 3) * 3))
| SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[4], vehicle->peep_tshirt_colours[5]);
sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
}
if (vehicle->num_peeps > 6)
{
image_id = (baseImage_id + (((directionOffset + 3) & 3) * 3))
| SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[6], vehicle->peep_tshirt_colours[7]);
sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
if (vehicle->num_peeps > 2)
{
image_id = (baseImage_id + ((((imageDirection / 8) + 1) & 3) * 3))
| SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[2], vehicle->peep_tshirt_colours[3]);
sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
}
if (vehicle->num_peeps > 4)
{
image_id = (baseImage_id + ((((imageDirection / 8) + 2) & 3) * 3))
| SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[4], vehicle->peep_tshirt_colours[5]);
sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
}
if (vehicle->num_peeps > 6)
{
image_id = (baseImage_id + ((((imageDirection / 8) + 3) & 3) * 3))
| SPRITE_ID_PALETTE_COLOUR_2(vehicle->peep_tshirt_colours[6], vehicle->peep_tshirt_colours[7]);
sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
}
}
}

View File

@ -35,6 +35,12 @@ void vehicle_visual_roto_drop(
paint_session* session, int32_t x, int32_t imageDirection, int32_t y, int32_t z, const rct_vehicle* vehicle,
const rct_ride_entry_vehicle* vehicleEntry)
{
auto imageFlags = SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour);
if (vehicle->IsGhost())
{
imageFlags = CONSTRUCTION_MARKER;
}
int32_t image_id;
int32_t baseImage_id = (vehicleEntry->base_image_id + 4) + ((vehicle->animation_frame / 4) & 0x3);
if (vehicle->restraints_position >= 64)
@ -44,39 +50,42 @@ void vehicle_visual_roto_drop(
}
// Draw back:
image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour);
image_id = baseImage_id | imageFlags;
sub_98197C(session, image_id, 0, 0, 2, 2, 41, z, -11, -11, z + 1);
// Draw front:
image_id = (baseImage_id + 4) | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour);
image_id = (baseImage_id + 4) | imageFlags;
sub_98197C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
uint8_t riding_peep_sprites[64];
std::fill_n(riding_peep_sprites, sizeof(riding_peep_sprites), 0xFF);
for (int32_t i = 0; i < vehicle->num_peeps; i++)
if (vehicle->num_peeps > 0 && !vehicle->IsGhost())
{
uint8_t cl = (i & 3) * 16;
cl += (i & 0xFC);
cl += vehicle->animation_frame / 4;
cl += (imageDirection / 8) * 16;
cl &= 0x3F;
riding_peep_sprites[cl] = vehicle->peep_tshirt_colours[i];
}
// Draw riding peep sprites in back to front order:
for (int32_t j = 0; j <= 48; j++)
{
int32_t i = (j % 2) ? (48 - (j / 2)) : (j / 2);
if (riding_peep_sprites[i] != 0xFF)
uint8_t riding_peep_sprites[64];
std::fill_n(riding_peep_sprites, sizeof(riding_peep_sprites), 0xFF);
for (int32_t i = 0; i < vehicle->num_peeps; i++)
{
baseImage_id = vehicleEntry->base_image_id + 20 + i;
if (vehicle->restraints_position >= 64)
uint8_t cl = (i & 3) * 16;
cl += (i & 0xFC);
cl += vehicle->animation_frame / 4;
cl += (imageDirection / 8) * 16;
cl &= 0x3F;
riding_peep_sprites[cl] = vehicle->peep_tshirt_colours[i];
}
// Draw riding peep sprites in back to front order:
for (int32_t j = 0; j <= 48; j++)
{
int32_t i = (j % 2) ? (48 - (j / 2)) : (j / 2);
if (riding_peep_sprites[i] != 0xFF)
{
baseImage_id += 64;
baseImage_id += vehicle->restraints_position / 64;
baseImage_id = vehicleEntry->base_image_id + 20 + i;
if (vehicle->restraints_position >= 64)
{
baseImage_id += 64;
baseImage_id += vehicle->restraints_position / 64;
}
image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_1(riding_peep_sprites[i]);
sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
}
image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_1(riding_peep_sprites[i]);
sub_98199C(session, image_id, 0, 0, 16, 16, 41, z, -5, -5, z + 1);
}
}

View File

@ -223,10 +223,15 @@ void vehicle_visual_river_rapids(
const vehicle_boundbox* bb = &_riverRapidsBoundbox[j];
image_id = baseImage_id | SPRITE_ID_PALETTE_COLOUR_2(vehicle->colours.body_colour, vehicle->colours.trim_colour);
if (vehicle->IsGhost())
{
image_id &= 0x7FFFF;
image_id |= CONSTRUCTION_MARKER;
}
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 (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0)
if (session->DPI.zoom_level < 2 && vehicle->num_peeps > 0 && !vehicle->IsGhost())
{
// Draw peeps: (this particular vehicle doesn't sort them back to front like others so the back ones sometimes clip, but
// that's how the original does it...)

View File

@ -24,6 +24,12 @@ void vehicle_visual_submarine(
paint_session* session, int32_t x, int32_t imageDirection, int32_t y, int32_t z, const rct_vehicle* vehicle,
const rct_ride_entry_vehicle* vehicleEntry)
{
auto imageFlags = SPRITE_ID_PALETTE_COLOUR_3(vehicle->colours.body_colour, vehicle->colours.trim_colour);
if (vehicle->IsGhost())
{
imageFlags = CONSTRUCTION_MARKER;
}
int32_t baseImage_id = imageDirection;
int32_t image_id;
if (vehicle->restraints_position >= 64)
@ -53,8 +59,7 @@ void vehicle_visual_submarine(
vehicle_boundbox bb = VehicleBoundboxes[vehicleEntry->draw_order][imageDirection / 2];
image_id = baseImage_id | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24)
| IMAGE_TYPE_REMAP_2_PLUS;
image_id = baseImage_id | imageFlags;
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)
@ -62,8 +67,7 @@ void vehicle_visual_submarine(
ps->tertiary_colour = vehicle->colours_extended;
}
image_id = (baseImage_id + 1) | (vehicle->colours.body_colour << 19) | (vehicle->colours.trim_colour << 24)
| IMAGE_TYPE_REMAP_2_PLUS;
image_id = (baseImage_id + 1) | imageFlags;
ps = sub_98197C(session, image_id, 0, 0, bb.length_x, bb.length_y, 2, z, bb.offset_x, bb.offset_y, bb.offset_z + z - 10);
if (ps != nullptr)
{

View File

@ -843,7 +843,13 @@ enum
SPR_G2_MULTIPLAYER_SYNC = SPR_G2_BEGIN + 124,
SPR_G2_MULTIPLAYER_DESYNC = SPR_G2_BEGIN + 125,
SPR_G2_CHAR_BEGIN = SPR_G2_BEGIN + 126,
SPR_G2_SIMULATE = SPR_G2_BEGIN + 126,
SPR_G2_RCT1_SIMULATE_BUTTON_0 = SPR_G2_BEGIN + 127,
SPR_G2_RCT1_SIMULATE_BUTTON_1 = SPR_G2_BEGIN + 128,
SPR_G2_RCT1_SIMULATE_BUTTON_2 = SPR_G2_BEGIN + 129,
SPR_G2_RCT1_SIMULATE_BUTTON_3 = SPR_G2_BEGIN + 130,
SPR_G2_CHAR_BEGIN = SPR_G2_BEGIN + 131,
SPR_G2_AE_UPPER = SPR_G2_CHAR_BEGIN,
SPR_G2_AE_LOWER = SPR_G2_CHAR_BEGIN + 1,

View File

@ -435,3 +435,9 @@ StationObject* ride_get_station_object(const Ride* ride)
{
return nullptr;
}
bool rct_vehicle::IsGhost() const
{
auto r = get_ride(ride);
return r != nullptr && r->status == RIDE_STATUS_SIMULATING;
}