diff --git a/src/ride/track.c b/src/ride/track.c index 8b364ff68f..09987b57d2 100644 --- a/src/ride/track.c +++ b/src/ride/track.c @@ -19,6 +19,7 @@ *****************************************************************************/ #include "../addresses.h" +#include "../audio/audio.h" #include "../game.h" #include "../interface/viewport.h" #include "../localisation/localisation.h" @@ -27,6 +28,7 @@ #include "../util/sawyercoding.h" #include "../util/util.h" #include "../world/park.h" +#include "../windows/error.h" #include "ride.h" #include "track.h" @@ -713,12 +715,12 @@ void load_track_scenery_objects(){ if (track_design->type == RIDE_TYPE_MAZE){ // Skip all of the maze track elements - while (*(uint32*)track_elements != 0)track_elements += 4; - track_elements += 4; + while (*(uint32*)track_elements != 0)track_elements += sizeof(rct_maze_element); + track_elements += sizeof(rct_maze_element); } else{ // Skip track_elements - while (*track_elements != 255) track_elements += 2; + while (*track_elements != 255) track_elements += sizeof(rct_track_element); track_elements++; // Skip entrance exit elements @@ -727,11 +729,13 @@ void load_track_scenery_objects(){ } while (*track_elements != 255){ - if (!find_object_in_entry_group((rct_object_entry*)track_elements, &entry_type, &entry_index)){ - object_load(-1, (rct_object_entry*)track_elements, 0); + rct_track_scenery* scenery_entry = (rct_track_scenery*)track_elements; + + if (!find_object_in_entry_group(&scenery_entry->scenery_object, &entry_type, &entry_index)){ + object_load(-1, &scenery_entry->scenery_object, 0); } // Skip object and location/direction/colour - track_elements += sizeof(rct_object_entry) + 6; + scenery_entry += sizeof(rct_track_scenery); } sub_6A9FC0(); @@ -1166,3 +1170,182 @@ int track_is_connected_by_shape(rct_map_element *a, rct_map_element *b) return aBank == bBank && aAngle == bAngle; } + +/* Based on rct2: 0x006D2897 */ +int copy_scenery_to_track(uint8** track_pointer){ + rct_track_scenery* track_scenery = (rct_track_scenery*)(*track_pointer - 1); + rct_track_scenery* scenery_source = RCT2_ADDRESS(0x009DA193, rct_track_scenery); + + while (1){ + int ebx = 0; + memcpy(track_scenery, scenery_source, sizeof(rct_track_scenery)); + if ((track_scenery->scenery_object.flags & 0xFF) == 0xFF) break; + + //0x00F4414D is direction of track? + if ((track_scenery->scenery_object.flags & 0xF) == OBJECT_TYPE_PATHS){ + + uint8 slope = (track_scenery->flags & 0x60) >> 5; + slope -= RCT2_GLOBAL(0x00F4414D, uint8); + + track_scenery->flags &= 0x9F; + track_scenery->flags |= ((slope & 3) << 5); + + // Direction of connection on path + uint8 direction = track_scenery->flags & 0xF; + // Rotate the direction by the track direction + direction = ((direction << 4) >> RCT2_GLOBAL(0x00F4414D, uint8)); + + track_scenery->flags &= 0xF0; + track_scenery->flags |= (direction & 0xF) | (direction >> 4); + + } + else if ((track_scenery->scenery_object.flags & 0xF) == OBJECT_TYPE_WALLS){ + uint8 direction = track_scenery->flags & 3; + + direction -= RCT2_GLOBAL(0x00F4414D, uint8); + + track_scenery->flags &= 0xFC; + track_scenery->flags |= (direction & 3); + } + else { + uint8 direction = track_scenery->flags & 3; + uint8 quadrant = (track_scenery->flags & 0xC) >> 2; + + direction -= RCT2_GLOBAL(0x00F4414D, uint8); + quadrant -= RCT2_GLOBAL(0x00F4414D, uint8); + + track_scenery->flags &= 0xF0; + track_scenery->flags |= (direction & 3) | ((quadrant & 3) << 2); + } + int x = track_scenery->x * 32 - RCT2_GLOBAL(0x00F44142, sint16); + int y = track_scenery->y * 32 - RCT2_GLOBAL(0x00F44144, sint16); + + switch (RCT2_GLOBAL(0x00F4414D, uint8)){ + case 0: + break; + case 1: + { + int temp_y = y; + y = x; + x = -y; + } + break; + case 2: + x = -x; + y = -y; + break; + case 3: + { + int temp_x = x; + x = y; + y = -x; + } + break; + } + + x /= 32; + y /= 32; + + if (x > 127 || y > 127 || x < -126 || y < -126){ + window_error_open(3346, 3347); + return 0; + } + + track_scenery->x = x; + track_scenery->y = y; + + int z = track_scenery->z * 8 - RCT2_GLOBAL(0xF44146, sint16); + + z /= 8; + + if (z > 127 || z < -126){ + window_error_open(3346, 3347); + return 0; + } + + track_scenery->z = z; + + track_scenery++; + scenery_source++; + } + + *track_pointer = (uint8*)track_scenery; + //Skip end of scenery elements byte + (*track_pointer)++; + return 1; +} + +/* rct2: 0x006D2804 & 0x006D264D */ +int save_track_design(uint8 rideIndex){ + rct_ride* ride = GET_RIDE(rideIndex); + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)){ + window_error_open(3346, RCT2_GLOBAL(0x141E9AC, rct_string_id)); + return 0; + } + + if (ride->ratings.excitement == 0xFFFF){ + window_error_open(3346, RCT2_GLOBAL(0x141E9AC, rct_string_id)); + return 0; + } + + if (!(RCT2_ADDRESS(0x0097CF40, uint32)[ride->type] & 0x10000000)){ + window_error_open(3346, RCT2_GLOBAL(0x141E9AC, rct_string_id)); + return 0; + } + + if (RCT2_CALLPROC_X(0x006CE44F, 0, 0, 0, rideIndex, 0, 0, 0) & 0x100){ + window_error_open(3346, RCT2_GLOBAL(0x141E9AC, rct_string_id)); + return 0; + } + + uint8* track_pointer = RCT2_GLOBAL(0x00F44058, uint8*); + if (RCT2_GLOBAL(0x009DEA6F, uint8) & 1){ + if (!copy_scenery_to_track(&track_pointer)) + return 0; + } + + while (track_pointer < RCT2_ADDRESS(0x009DE217, uint8))*track_pointer++ = 0; + + char track_name[MAX_PATH]; + // Get track name + format_string(track_name, ride->name, &ride->name_arguments); + + char path[MAX_PATH]; + subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_TRACKS_PATH, char), track_name); + + strcat(path, ".TD6"); + + // Save track design + format_string(RCT2_ADDRESS(0x141ED68, char), 2306, NULL); + + // Track design files + format_string(RCT2_ADDRESS(0x141EE68, char), 2305, NULL); + + pause_sounds(); + + int result = platform_open_common_file_dialog( + 0, + RCT2_ADDRESS(0x141ED68, char), + path, + "*.TD?", + RCT2_ADDRESS(0x141EE68, char)); + + unpause_sounds(); + + if (result == 0){ + ride_list_item item = { .type = 0xFD, .entry_index = 0 }; + track_load_list(item); + return 1; + } + + // Until 0x006771DC is finished we required to copy the path name. + strcpy(RCT2_ADDRESS(0x141EF68, char), path); + // This is the function that actually saves the track to a file + RCT2_CALLPROC_EBPSAFE(0x006771DC); + + ride_list_item item = { .type = 0xFC, .entry_index = 0 }; + track_load_list(item); + gfx_invalidate_screen(); + return 1; +} diff --git a/src/ride/track.h b/src/ride/track.h index fca8678206..7778271ee6 100644 --- a/src/ride/track.h +++ b/src/ride/track.h @@ -69,6 +69,17 @@ typedef struct{ uint8 flags; }rct_track_element; +/* Track Scenery entry size: 0x16 */ +typedef struct{ + rct_object_entry scenery_object; // 0x00 + uint8 x; // 0x10 + uint8 y; // 0x11 + uint8 z; // 0x12 + uint8 flags; // 0x13 direction quadrant tertiary colour + uint8 primary_colour; // 0x14 + uint8 secondary_colour; // 0x15 +}rct_track_scenery; + enum{ TRACK_ELEMENT_FLAG_CHAIN_LIFT = (1<<7), TRACK_ELEMENT_FLAG_INVERTED = (1<<6), @@ -216,5 +227,6 @@ int track_delete(); void reset_track_list_cache(); int track_is_connected_by_shape(rct_map_element *a, rct_map_element *b); int sub_6D01B3(int bl, int x, int y, int z); +int save_track_design(uint8 rideIndex); #endif diff --git a/src/windows/ride.c b/src/windows/ride.c index 8a642075fb..b2baeaf226 100644 --- a/src/windows/ride.c +++ b/src/windows/ride.c @@ -28,10 +28,12 @@ #include "../peep/staff.h" #include "../ride/ride.h" #include "../ride/ride_data.h" +#include "../ride/track.h" #include "../sprites.h" #include "../windows/error.h" #include "../world/map.h" #include "../world/sprite.h" +#include "../audio/audio.h" #include "dropdown.h" #define var_496(w) RCT2_GLOBAL((int)w + 0x496, uint16) @@ -4390,6 +4392,50 @@ static void window_ride_music_paint() #pragma region Measurements +/* rct2: 0x006D2804 when al == 0*/ +static void cancel_scenery_selection(){ + RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) &= ~(1 << 2); + RCT2_GLOBAL(0x9DEA6F, uint8) &= ~(1 << 0); + unpause_sounds(); + + rct_window* main_w = window_get_main(); + + if (main_w){ + main_w->viewport->flags &= ~(VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); + } + + gfx_invalidate_screen(); + tool_cancel(); +} + +/* rct2: 0x006D27A3 */ +static void setup_scenery_selection(rct_window* w){ + rct_ride* ride = GET_RIDE(w->number); + + if (RCT2_GLOBAL(0x009DEA6F, uint8) & 1){ + cancel_scenery_selection(); + } + + while (tool_set(w, 0, 12)); + + RCT2_GLOBAL(0x00F64DE8, uint8) = (uint8)w->number; + RCT2_GLOBAL(0x009DA193, uint8) = 0xFF; + + RCT2_GLOBAL(0x00F63674, sint32) = -1; + RCT2_GLOBAL(RCT2_ADDRESS_GAME_PAUSED, uint8) |= (1 << 2); + RCT2_GLOBAL(0x009DEA6F, uint8) |= 1; + + pause_sounds(); + + rct_window* w_main = window_get_main(); + + if (w_main){ + w_main->viewport->flags |= (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE); + } + + gfx_invalidate_screen(); +} + /** * * rct2: 0x006D3026 @@ -4408,15 +4454,6 @@ static void window_ride_measurements_design_select_nearby_scenery() RCT2_CALLPROC_EBPSAFE(0x006D303D); } -/** - * - * rct2: 0x006AD4CD - */ -static void window_ride_measurements_design_save(rct_window *w) -{ - RCT2_CALLPROC_X(0x006D2804, 1, 0, 0, 0, (int)w, 0, 0); -} - /** * * rct2: 0x006AD4DA @@ -4424,7 +4461,18 @@ static void window_ride_measurements_design_save(rct_window *w) static void window_ride_measurements_design_cancel() { if (RCT2_GLOBAL(0x009DEA6F, uint8) & 1) - RCT2_CALLPROC_X(0x006D2804, 0, 0, 0, 0, 0, 0, 0); + cancel_scenery_selection(); +} + +/** + * + * rct2: 0x006AD4CD + */ +static void window_ride_measurements_design_save(rct_window *w) +{ + if (save_track_design((uint8)w->number) == 0) return; + + window_ride_measurements_design_cancel(); } /** @@ -4536,9 +4584,9 @@ static void window_ride_measurements_dropdown() dropdownIndex = RCT2_GLOBAL(0x009DEBA2, sint16); if (dropdownIndex == 0) - RCT2_CALLPROC_X(0x006D264D, 0, 0, 0, 0, (int)w, 0, 0); + save_track_design((uint8)w->number); else - RCT2_CALLPROC_X(0x006D27A3, 0, 0, 0, 0, (int)w, 0, 0); + setup_scenery_selection(w); } /**