diff --git a/openrct2.vcxproj b/openrct2.vcxproj index aa5444cb99..0edaee5ecb 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -182,6 +182,7 @@ + diff --git a/src/object_list.c b/src/object_list.c index a6305580c1..e441841654 100644 --- a/src/object_list.c +++ b/src/object_list.c @@ -337,7 +337,7 @@ void object_list_load() ride_list_item ride_list; ride_list.entry_index = 0xFC; ride_list.type = 0xFC; - track_load_list(ride_list); + // track_load_list(ride_list); object_list_examine(); } diff --git a/src/ride/track_design.c b/src/ride/track_design.c index 8e912082fa..4751c7d9b7 100644 --- a/src/ride/track_design.c +++ b/src/ride/track_design.c @@ -20,18 +20,225 @@ static bool track_design_open_from_buffer(rct_track_td6 *td6, uint8 *src, size_t srcLength); -#define TRACK_MAX_SAVED_MAP_ELEMENTS 1500 - rct_map_element **gTrackSavedMapElements = (rct_map_element**)0x00F63674; rct_track_td6 *gActiveTrackDesign; money32 gTrackDesignCost; uint8 gTrackDesignPlaceFlags; -static bool track_save_should_select_scenery_around(int rideIndex, rct_map_element *mapElement); -static void track_save_select_nearby_scenery_for_tile(int rideIndex, int cx, int cy); -static bool track_save_add_map_element(int interactionType, int x, int y, rct_map_element *mapElement); +static bool track_design_preview_backup_map(); +static void track_design_preview_restore_map(); +static void track_design_preview_clear_map(); -uint32* sub_6AB49A(rct_object_entry* entry){ +static bool td4_track_has_boosters(rct_track_td6* track_design, uint8* track_elements); + +static void copy(void *dst, uint8 **src, int length) +{ + memcpy(dst, *src, length); + *src += length; +} + +bool track_design_open(rct_track_td6 *td6, const utf8 *path) +{ + SDL_RWops *file = SDL_RWFromFile(path, "rb"); + if (file != NULL) { + // Read whole file into a buffer + size_t bufferLength = (size_t)SDL_RWsize(file); + uint8 *buffer = (uint8*)malloc(bufferLength); + if (buffer == NULL) { + log_error("Unable to allocate memory for track design file."); + SDL_RWclose(file); + return false; + } + SDL_RWread(file, buffer, bufferLength, 1); + SDL_RWclose(file); + + if (!sawyercoding_validate_track_checksum(buffer, bufferLength)) { + log_error("Track checksum failed."); + free(buffer); + return false; + } + + // Decode the track data + uint8 *decoded = malloc(0x10000); + size_t decodedLength = sawyercoding_decode_td6(buffer, decoded, bufferLength); + free(buffer); + decoded = realloc(decoded, decodedLength); + if (decoded == NULL) { + log_error("failed to realloc"); + } else { + track_design_open_from_buffer(td6, decoded, decodedLength); + free(decoded); + return true; + } + } + return false; +} + +static bool track_design_open_from_buffer(rct_track_td6 *td6, uint8 *src, size_t srcLength) +{ + uint8 *readPtr = src; + + // Clear top of track_design as this is not loaded from the td4 files + memset(&td6->track_spine_colour, 0, 67); + + // Read start of track_design + copy(td6, &readPtr, 32); + + uint8 version = td6->version_and_colour_scheme >> 2; + if (version > 2) { + log_error("Unsupported track design."); + return false; + } + + // In TD6 there are 32 sets of two byte vehicle colour specifiers + // In TD4 there are 12 sets so the remaining 20 need to be read + if (version == 2) { + copy(&td6->vehicle_colours[12], &readPtr, 40); + } + + copy(&td6->pad_48, &readPtr, 24); + + // In TD4 (version AA/CF) and TD6 both start actual track data at 0xA3 + if (version > 0) { + copy(&td6->track_spine_colour, &readPtr, version == 1 ? 140 : 67); + } + + // Read the actual track data to memory directly after the passed in TD6 struct + size_t elementDataLength = srcLength - (readPtr - src); + uint8 *elementData = malloc(elementDataLength); + if (elementData == NULL) { + log_error("Unable to allocate memory for TD6 element data."); + return false; + } + copy(elementData, &readPtr, elementDataLength); + td6->elements = elementData; + td6->elementsSize = elementDataLength; + + uint8 *final_track_element_location = elementData + elementDataLength; + + // TD4 files require some extra work to be recognised as TD6. + if (version < 2) { + // Set any element passed the tracks to 0xFF + if (td6->type == RIDE_TYPE_MAZE) { + rct_maze_element* maze_element = (rct_maze_element*)elementData; + while (maze_element->all != 0) { + maze_element++; + } + maze_element++; + memset(maze_element, 255, final_track_element_location - (uint8*)maze_element); + } else { + rct_track_element* track_element = (rct_track_element*)elementData; + while (track_element->type != 255) { + track_element++; + } + memset(((uint8*)track_element) + 1, 255, final_track_element_location - (uint8*)track_element); + } + + // Convert the colours from RCT1 to RCT2 + for (int i = 0; i < 32; i++) { + rct_vehicle_colour *vehicleColour = &td6->vehicle_colours[i]; + vehicleColour->body_colour = rct1_get_colour(vehicleColour->body_colour); + vehicleColour->trim_colour = rct1_get_colour(vehicleColour->trim_colour); + } + + td6->track_spine_colour_rct1 = rct1_get_colour(td6->track_spine_colour_rct1); + td6->track_rail_colour_rct1 = rct1_get_colour(td6->track_rail_colour_rct1); + td6->track_support_colour_rct1 = rct1_get_colour(td6->track_support_colour_rct1); + + for (int i = 0; i < 4; i++) { + td6->track_spine_colour[i] = rct1_get_colour(td6->track_spine_colour[i]); + td6->track_rail_colour[i] = rct1_get_colour(td6->track_rail_colour[i]); + td6->track_support_colour[i] = rct1_get_colour(td6->track_support_colour[i]); + } + + // Highest drop height is 1bit = 3/4 a meter in TD6 + // Highest drop height is 1bit = 1/3 a meter in TD4 + // Not sure if this is correct?? + td6->highest_drop_height >>= 1; + + // If it has boosters then sadly track has to be discarded. + if (td4_track_has_boosters(td6, elementData)) { + log_error("Track design contains RCT1 boosters which are not yet supported."); + free(td6->elements); + td6->elements = NULL; + return false; + } + + // Convert RCT1 ride type to RCT2 ride type + uint8 rct1RideType = td6->type; + if (rct1RideType == RCT1_RIDE_TYPE_WOODEN_ROLLER_COASTER) { + td6->type = RIDE_TYPE_WOODEN_ROLLER_COASTER; + } else if (rct1RideType == RCT1_RIDE_TYPE_STEEL_CORKSCREW_ROLLER_COASTER) { + if (td6->vehicle_type == RCT1_VEHICLE_TYPE_HYPERCOASTER_TRAIN) { + if (td6->ride_mode == RCT1_RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE) { + td6->ride_mode = RIDE_MODE_CONTINUOUS_CIRCUIT; + } + } + } + + // All TD4s that use powered launch use the type that doesn't pass the station. + if (td6->ride_mode == RCT1_RIDE_MODE_POWERED_LAUNCH) { + td6->ride_mode = RIDE_MODE_POWERED_LAUNCH; + } + + // Convert RCT1 vehicle type to RCT2 vehicle type + rct_object_entry *vehicle_object; + if (td6->type == RIDE_TYPE_MAZE) { + vehicle_object = RCT2_ADDRESS(0x0097F66C, rct_object_entry); + } else { + int vehicle_type = td6->vehicle_type; + if (vehicle_type == RCT1_VEHICLE_TYPE_INVERTED_COASTER_TRAIN && + td6->type == RIDE_TYPE_INVERTED_ROLLER_COASTER + ) { + vehicle_type = RCT1_VEHICLE_TYPE_4_ACROSS_INVERTED_COASTER_TRAIN; + } + vehicle_object = &RCT2_ADDRESS(0x0097F0DC, rct_object_entry)[vehicle_type]; + } + memcpy(&td6->vehicle_object, vehicle_object, sizeof(rct_object_entry)); + + // Further vehicle colour fixes + for (int i = 0; i < 32; i++) { + td6->vehicle_additional_colour[i] = td6->vehicle_colours[i].trim_colour; + + // RCT1 river rapids always had black seats. + if (rct1RideType == RCT1_RIDE_TYPE_RIVER_RAPIDS) { + td6->vehicle_colours[i].trim_colour = COLOUR_BLACK; + } + } + + td6->space_required_x = 255; + td6->space_required_y = 255; + td6->lift_hill_speed_num_circuits = 5; + } + + td6->var_50 = min( + td6->var_50, + RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + 5 + (td6->type * 8), uint8) + ); + + return true; +} + +/** + * + * rct2: 0x00677530 + * Returns true if it has booster track elements + */ +static bool td4_track_has_boosters(rct_track_td6* track_design, uint8* track_elements) +{ + if (track_design->type != RCT1_RIDE_TYPE_HEDGE_MAZE) { + rct_track_element *track_element = (rct_track_element*)track_elements; + for (; track_element->type != 0xFF; track_element++) { + if (track_element->type == RCT1_TRACK_ELEM_BOOSTER) { + return true; + } + } + } + return false; +} + +uint32* sub_6AB49A(rct_object_entry* entry) +{ rct_object_entry* object_list_entry = object_list_find(entry); if (object_list_entry == NULL) return NULL; @@ -40,13 +247,8 @@ uint32* sub_6AB49A(rct_object_entry* entry){ return (((uint32*)object_get_next(object_list_entry)) - 1); } -static void get_track_idx_path(char *outPath) +void track_update_max_min_coordinates(sint16 x, sint16 y, sint16 z) { - platform_get_user_directory(outPath, NULL); - strcat(outPath, "tracks.idx"); -} - -void track_update_max_min_coordinates(sint16 x, sint16 y, sint16 z){ if (x < RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MIN, sint16)){ RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MIN, sint16) = x; } @@ -72,78 +274,7 @@ void track_update_max_min_coordinates(sint16 x, sint16 y, sint16 z){ } } -static void track_list_query_directory(int *outTotalFiles){ - int enumFileHandle; - file_info enumFileInfo; - - *outTotalFiles = 0; - - // Enumerate through each track in the directory - enumFileHandle = platform_enumerate_files_begin(RCT2_ADDRESS(RCT2_ADDRESS_TRACKS_PATH, char)); - if (enumFileHandle == INVALID_HANDLE) - return; - - while (platform_enumerate_files_next(enumFileHandle, &enumFileInfo)) { - (*outTotalFiles)++; - } - platform_enumerate_files_end(enumFileHandle); -} - -static int track_list_cache_save(int fileCount, uint8* track_list_cache, uint32 track_list_size) -{ - char path[MAX_PATH]; - SDL_RWops *file; - - log_verbose("saving track list cache (tracks.idx)"); - get_track_idx_path(path); - - file = SDL_RWFromFile(path, "wb"); - if (file == NULL) { - log_error("Failed to save %s", path); - return 0; - } - - SDL_RWwrite(file, &fileCount, sizeof(int), 1); - SDL_RWwrite(file, track_list_cache, track_list_size, 1); - uint8 last_entry = 0xFE; - SDL_RWwrite(file, &last_entry, 1, 1); - SDL_RWclose(file); - return 1; -} - -static uint8* track_list_cache_load(int totalFiles) -{ - char path[MAX_PATH]; - SDL_RWops *file; - - log_verbose("loading track list cache (tracks.idx)"); - get_track_idx_path(path); - file = SDL_RWFromFile(path, "rb"); - if (file == NULL) { - log_error("Failed to load %s", path); - return 0; - } - - uint8* track_list_cache; - uint32 fileCount; - // Remove 4 for the file count variable - long track_list_size = (long)SDL_RWsize(file) - 4; - - if (track_list_size < 0) return 0; - - SDL_RWread(file, &fileCount, 4, 1); - - if (fileCount != totalFiles){ - SDL_RWclose(file); - log_verbose("Track file count is different."); - return 0; - } - - track_list_cache = malloc(track_list_size); - SDL_RWread(file, track_list_cache, track_list_size, 1); - SDL_RWclose(file); - return track_list_cache; -} +#ifdef ADSIASD void track_list_populate(ride_list_item item, uint8* track_list_cache){ uint8* track_pointer = track_list_cache; @@ -289,302 +420,7 @@ void track_load_list(ride_list_item item) free(track_list_cache); } -static void copy(void *dst, uint8 **src, int length) -{ - memcpy(dst, *src, length); - *src += length; -} - -/** - * - * rct2: 0x00677530 - * Returns 1 if it has booster track elements - */ -uint8 td4_track_has_boosters(rct_track_td6* track_design, uint8* track_elements){ - if (track_design->type == RCT1_RIDE_TYPE_HEDGE_MAZE) - return 0; - - rct_track_element* track_element = (rct_track_element*)track_elements; - - for (; track_element->type != 0xFF; track_element++){ - if (track_element->type == RCT1_TRACK_ELEM_BOOSTER) - return 1; - } - - return 0; -} - -/** - * - * rct2: 0x0067726A - * path: 0x0141EF68 - */ -rct_track_td6* load_track_design(const char *path) -{ - SDL_RWops *fp; - int fpLength; - uint8 *fpBuffer, *decoded, *src; - int i, decodedLength; - uint8* edi; - - RCT2_GLOBAL(0x009AAC54, uint8) = 1; - - fp = SDL_RWFromFile(path, "rb"); - if (fp == NULL) - return 0; - - char* track_name_pointer = (char*)path; - while (*track_name_pointer++ != '\0'); - const char separator = platform_get_path_separator(); - while (*--track_name_pointer != separator); - char* default_name = RCT2_ADDRESS(0x009E3504, char); - // Copy the track name for use as the default name of this ride - while (*++track_name_pointer != '.')*default_name++ = *track_name_pointer; - *default_name++ = '\0'; - - // Read whole file into a buffer - fpLength = (int)SDL_RWsize(fp); - fpBuffer = malloc(fpLength); - SDL_RWread(fp, fpBuffer, fpLength, 1); - SDL_RWclose(fp); - - // Validate the checksum - // Not the same checksum algorithm as scenarios and saved games - if (!sawyercoding_validate_track_checksum(fpBuffer, fpLength)){ - log_error("Track checksum failed."); - return 0; - } - - // Decode the track data - decoded = malloc(0x10000); - decodedLength = sawyercoding_decode_td6(fpBuffer, decoded, fpLength); - decoded = realloc(decoded, decodedLength); - if (decoded == NULL) { - log_error("failed to realloc"); - return 0; - } - free(fpBuffer); - - rct_track_td6* track_design = RCT2_ADDRESS(0x009D8178, rct_track_td6); - // Read decoded data - src = decoded; - // Clear top of track_design as this is not loaded from the td4 files - memset(&track_design->track_spine_colour, 0, 67); - // Read start of track_design - copy(track_design, &src, 32); - - uint8 version = track_design->version_and_colour_scheme >> 2; - - if (version > 2){ - free(decoded); - return NULL; - } - - // In td6 there are 32 sets of two byte vehicle colour specifiers - // In td4 there are 12 sets so the remaining 20 need to be read. - if (version == 2) - copy(&track_design->vehicle_colours[12], &src, 40); - - copy(&track_design->pad_48, &src, 24); - - // In td4 (version AA/CF) and td6 both start actual track data at 0xA3 - if (version > 0) - copy(&track_design->track_spine_colour, &src, version == 1 ? 140 : 67); - - uint8* track_elements = RCT2_ADDRESS(0x9D821B, uint8); - int len = decodedLength - (src - decoded); - // Read the actual track data. - copy(track_elements, &src, len); - - uint8* final_track_element_location = track_elements + len; - free(decoded); - - // td4 files require some work to be recognised as td6. - if (version < 2) { - - // Set any element passed the tracks to 0xFF - if (track_design->type == RIDE_TYPE_MAZE) { - rct_maze_element* maze_element = (rct_maze_element*)track_elements; - while (maze_element->all != 0) { - maze_element++; - } - maze_element++; - memset(maze_element, 255, final_track_element_location - (uint8*)maze_element); - } - else { - rct_track_element* track_element = (rct_track_element*)track_elements; - while (track_element->type != 255) { - track_element++; - } - memset(((uint8*)track_element) + 1, 255, final_track_element_location - (uint8*)track_element); - - } - - // Edit the colours to use the new versions - // Unsure why it is 67 - edi = (uint8*)&track_design->vehicle_colours; - for (i = 0; i < 67; i++, edi++) - *edi = rct1_get_colour(*edi); - - // Edit the colours to use the new versions - edi = (uint8*)&track_design->track_spine_colour; - for (i = 0; i < 12; i++, edi++) - *edi = rct1_get_colour(*edi); - - // Highest drop height is 1bit = 3/4 a meter in td6 - // Highest drop height is 1bit = 1/3 a meter in td4 - // Not sure if this is correct?? - track_design->highest_drop_height >>= 1; - - // If it has boosters then sadly track has to be discarded. - if (td4_track_has_boosters(track_design, track_elements)) - track_design->type = RIDE_TYPE_NULL; - - if (track_design->type == RCT1_RIDE_TYPE_WOODEN_ROLLER_COASTER) - track_design->type = RIDE_TYPE_WOODEN_ROLLER_COASTER; - - if (track_design->type == RIDE_TYPE_CORKSCREW_ROLLER_COASTER) { - if (track_design->vehicle_type == 79) { - if (track_design->ride_mode == 2) - track_design->ride_mode = 1; - } - } - - // All TD4s that use powered launch use the type that doesn't pass the station. - if (track_design->ride_mode == RCT1_RIDE_MODE_POWERED_LAUNCH) - track_design->ride_mode = RIDE_MODE_POWERED_LAUNCH; - - rct_object_entry* vehicle_object; - if (track_design->type == RIDE_TYPE_MAZE) { - vehicle_object = RCT2_ADDRESS(0x0097F66C, rct_object_entry); - } else { - int vehicle_type = track_design->vehicle_type; - if (vehicle_type == RCT1_VEHICLE_TYPE_INVERTED_COASTER_TRAIN && track_design->type == RIDE_TYPE_INVERTED_ROLLER_COASTER) - vehicle_type = RCT1_VEHICLE_TYPE_4_ACROSS_INVERTED_COASTER_TRAIN; - vehicle_object = &RCT2_ADDRESS(0x0097F0DC, rct_object_entry)[vehicle_type]; - } - - memcpy(&track_design->vehicle_object, vehicle_object, sizeof(rct_object_entry)); - for (i = 0; i < 32; i++) { - track_design->vehicle_additional_colour[i] = track_design->vehicle_colours[i].trim_colour; - - // RCT1 river rapids always had black seats. - if (track_design->type == RCT1_RIDE_TYPE_RIVER_RAPIDS) - track_design->vehicle_colours[i].trim_colour = 0; - } - - track_design->space_required_x = 255; - track_design->space_required_y = 255; - track_design->lift_hill_speed_num_circuits = 5; - } - - track_design->var_50 = min( - track_design->var_50, - RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + 5 + (track_design->type * 8), uint8) - ); - - return track_design; -} - -/** - * - * rct2: 0x006D1DCE - */ -void reset_track_list_cache(){ - int* track_list_cache = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_DESIGN_INDEX_CACHE, int); - for (int i = 0; i < 4; ++i){ - track_list_cache[i] = -1; - } - RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_NEXT_INDEX_CACHE, uint32) = 0; -} - -/** - * - * rct2: 0x006D1C68 - */ -int backup_map() -{ - RCT2_GLOBAL(0xF440ED, uint8*) = malloc(0xED600); - if (RCT2_GLOBAL(0xF440ED, uint32) == 0) return 0; - - RCT2_GLOBAL(0xF440F1, uint8*) = malloc(0x40000); - if (RCT2_GLOBAL(0xF440F1, uint32) == 0){ - free(RCT2_GLOBAL(0xF440ED, uint8*)); - return 0; - } - - RCT2_GLOBAL(0xF440F5, uint8*) = malloc(14); - if (RCT2_GLOBAL(0xF440F5, uint32) == 0){ - free(RCT2_GLOBAL(0xF440ED, uint8*)); - free(RCT2_GLOBAL(0xF440F1, uint8*)); - return 0; - } - - uint32* map_elements = RCT2_ADDRESS(RCT2_ADDRESS_MAP_ELEMENTS, uint32); - memcpy(RCT2_GLOBAL(0xF440ED, uint32*), map_elements, 0xED600); - - uint32* tile_map_pointers = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, uint32); - memcpy(RCT2_GLOBAL(0xF440F1, uint32*), tile_map_pointers, 0x40000); - - uint8* backup_info = RCT2_GLOBAL(0xF440F5, uint8*); - *(uint32*)backup_info = (uint32)gNextFreeMapElement; - *(uint16*)(backup_info + 4) = gMapSizeUnits; - *(uint16*)(backup_info + 6) = gMapSizeMinus2; - *(uint16*)(backup_info + 8) = gMapSize; - *(uint32*)(backup_info + 10) = get_current_rotation(); - return 1; -} - -/** - * - * rct2: 0x006D2378 - */ -void reload_map_backup() -{ - uint32* map_elements = RCT2_ADDRESS(RCT2_ADDRESS_MAP_ELEMENTS, uint32); - memcpy(map_elements, RCT2_GLOBAL(0xF440ED, uint32*), 0xED600); - - uint32* tile_map_pointers = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, uint32); - memcpy(tile_map_pointers, RCT2_GLOBAL(0xF440F1, uint32*), 0x40000); - - uint8* backup_info = RCT2_GLOBAL(0xF440F5, uint8*); - gNextFreeMapElement = (rct_map_element*)backup_info; - gMapSizeUnits = *(uint16*)(backup_info + 4); - gMapSizeMinus2 = *(uint16*)(backup_info + 6); - gMapSize = *(uint16*)(backup_info + 8); - gCurrentRotation = *(uint8*)(backup_info + 10); - - free(RCT2_GLOBAL(0xF440ED, uint8*)); - free(RCT2_GLOBAL(0xF440F1, uint8*)); - free(RCT2_GLOBAL(0xF440F5, uint8*)); -} - -/** - * - * rct2: 0x006D1D9A - */ -void blank_map(){ - - // These values were previously allocated in backup map but - // it seems more fitting to place in this function - gMapSizeUnits = 0x1FE0; - gMapSizeMinus2 = 0x20FE; - gMapSize = 0x100; - - rct_map_element* map_element; - for (int i = 0; i < MAX_TILE_MAP_ELEMENT_POINTERS; i++) { - map_element = GET_MAP_ELEMENT(i); - map_element->type = MAP_ELEMENT_TYPE_SURFACE; - map_element->flags = MAP_ELEMENT_FLAG_LAST_TILE; - map_element->base_height = 2; - map_element->clearance_height = 0; - map_element->properties.surface.slope = 0; - map_element->properties.surface.terrain = 0; - map_element->properties.surface.grass_length = 1; - map_element->properties.surface.ownership = OWNERSHIP_OWNED; - } - map_update_tile_pointers(); -} +#endif /** * @@ -1764,9 +1600,9 @@ bool sub_6D2189(rct_track_td6 *td6, money32 *cost, uint8 *rideId) void draw_track_preview(rct_track_td6 *td6, uint8** preview) { // Make a copy of the map - if (!backup_map())return; + if (!track_design_preview_backup_map())return; - blank_map(); + track_design_preview_clear_map(); if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER){ load_track_scenery_objects(); @@ -1776,7 +1612,7 @@ void draw_track_preview(rct_track_td6 *td6, uint8** preview) uint8 ride_id; if (!sub_6D2189(td6, &cost, &ride_id)) { memset(preview, 0, TRACK_PREVIEW_IMAGE_SIZE * 4); - reload_map_backup(); + track_design_preview_restore_map(); return; } gTrackDesignCost = cost; @@ -1883,9 +1719,11 @@ void draw_track_preview(rct_track_td6 *td6, uint8** preview) viewport_paint(view, dpi, left, top, right, bottom); sub_6D235B(ride_id); - reload_map_backup(); + track_design_preview_restore_map(); } +#ifdef fsdfds + /** * * I don't think preview is a necessary output argument. It can be obtained easily using the track design structure. @@ -1948,11 +1786,13 @@ rct_track_design *track_get_info(int index, uint8** preview) return trackDesign; } +#endif + /** * * rct2: 0x006D3664 */ -int track_rename(const char *text) +bool track_design_rename(const char *text) { const char* txt_chr = text; @@ -1965,7 +1805,7 @@ int track_rename(const char *text) case '?': // Invalid characters gGameCommandErrorText = STR_NEW_NAME_CONTAINS_INVALID_CHARACTERS; - return 0; + return false; } txt_chr++; } @@ -1981,20 +1821,20 @@ int track_rename(const char *text) if (!platform_file_move(old_path, new_path)) { gGameCommandErrorText = STR_ANOTHER_FILE_EXISTS_WITH_NAME_OR_FILE_IS_WRITE_PROTECTED; - return 0; + return false; } ride_list_item item = { 0xFC, 0 }; - track_load_list(item); + // track_load_list(item); item.type = RCT2_GLOBAL(0xF44158, uint8); item.entry_index = RCT2_GLOBAL(0xF44159, uint8); - track_load_list(item); + // track_load_list(item); - reset_track_list_cache(); + // reset_track_list_cache(); window_invalidate(w); - return 1; + return true; } /** @@ -2014,743 +1854,18 @@ int track_delete() } ride_list_item item = { 0xFC, 0 }; - track_load_list(item); + // track_load_list(item); item.type = RCT2_GLOBAL(0xF44158, uint8); item.entry_index = RCT2_GLOBAL(0xF44159, uint8); - track_load_list(item); + // track_load_list(item); - reset_track_list_cache(); + // reset_track_list_cache(); window_invalidate(w); return 1; } -/* 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 = ((uint8)track_scenery->x) * 32 - RCT2_GLOBAL(0x00F44142, sint16); - int y = ((uint8)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 = -temp_y; - } - break; - case 2: - x = -x; - y = -y; - break; - case 3: - { - int temp_x = x; - x = y; - y = -temp_x; - } - break; - } - - x /= 32; - y /= 32; - - if (x > 127 || y > 127 || x < -126 || y < -126){ - window_error_open(3346, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); - 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, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); - 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: 0x006CEAAE - */ -int maze_ride_to_td6(uint8 rideIndex, rct_track_td6* track_design, uint8* track_elements){ - rct_map_element* map_element = NULL; - uint8 map_found = 0; - - sint16 start_x, start_y; - - for (start_y = 0; start_y < 8192; start_y += 32){ - for (start_x = 0; start_x < 8192; start_x += 32){ - map_element = map_get_first_element_at(start_x / 32, start_y / 32); - - do{ - if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_TRACK) - continue; - if (map_element->properties.track.ride_index == rideIndex){ - map_found = 1; - break; - } - } while (!map_element_is_last_for_tile(map_element++)); - - if (map_found) - break; - } - if (map_found) - break; - } - - if (map_found == 0){ - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - RCT2_GLOBAL(0x00F44142, sint16) = start_x; - RCT2_GLOBAL(0x00F44144, sint16) = start_y; - RCT2_GLOBAL(0x00F44146, sint16) = map_element->base_height * 8; - - rct_maze_element* maze = (rct_maze_element*)track_elements; - - // x is defined here as we can start the search - // on tile start_x, start_y but then the next row - // must restart on 0 - for (sint16 y = start_y, x = start_x; y < 8192; y += 32){ - for (; x < 8192; x += 32){ - map_element = map_get_first_element_at(x / 32, y / 32); - - do{ - if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_TRACK) - continue; - if (map_element->properties.track.ride_index != rideIndex) - continue; - - maze->maze_entry = map_element->properties.track.maze_entry; - maze->x = (x - start_x) / 32; - maze->y = (y - start_y) / 32; - maze++; - - if (maze >= RCT2_ADDRESS(0x009DA151, rct_maze_element)){ - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - } while (!map_element_is_last_for_tile(map_element++)); - - } - x = 0; - } - - rct_ride* ride = get_ride(rideIndex); - uint16 location = ride->entrances[0]; - - if (location == 0xFFFF){ - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - sint16 x = (location & 0xFF) * 32; - sint16 y = ((location & 0xFF00) >> 8) * 32; - - map_element = map_get_first_element_at(x / 32, y / 32); - - do{ - if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_ENTRANCE) - continue; - if (map_element->properties.entrance.type != ENTRANCE_TYPE_RIDE_ENTRANCE) - continue; - if (map_element->properties.entrance.ride_index == rideIndex) - break; - } while (!map_element_is_last_for_tile(map_element++)); - // Add something that stops this from walking off the end - - uint8 entrance_direction = map_element->type & MAP_ELEMENT_DIRECTION_MASK; - maze->unk_2 = entrance_direction; - maze->type = 8; - - maze->x = (sint8)((x - start_x) / 32); - maze->y = (sint8)((y - start_y) / 32); - - maze++; - - location = ride->exits[0]; - - if (location == 0xFFFF){ - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - x = (location & 0xFF) * 32; - y = ((location & 0xFF00) >> 8) * 32; - - map_element = map_get_first_element_at(x / 32, y / 32); - - do{ - if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_ENTRANCE) - continue; - if (map_element->properties.entrance.type != ENTRANCE_TYPE_RIDE_EXIT) - continue; - if (map_element->properties.entrance.ride_index == rideIndex) - break; - } while (!map_element_is_last_for_tile(map_element++)); - // Add something that stops this from walking off the end - - uint8 exit_direction = map_element->type & MAP_ELEMENT_DIRECTION_MASK; - maze->unk_2 = exit_direction; - maze->type = 0x80; - - maze->x = (sint8)((x - start_x) / 32); - maze->y = (sint8)((y - start_y) / 32); - - maze++; - - maze->all = 0; - maze++; - - track_elements = (uint8*)maze; - *track_elements++ = 0xFF; - - RCT2_GLOBAL(0x00F44058, uint8*) = track_elements; - - // Save global vars as they are still used by scenery - sint16 start_z = RCT2_GLOBAL(0x00F44146, sint16); - sub_6D01B3(track_design, PTD_OPERATION_DRAW_OUTLINES, 0, 4096, 4096, 0); - RCT2_GLOBAL(0x00F44142, sint16) = start_x; - RCT2_GLOBAL(0x00F44144, sint16) = start_y; - RCT2_GLOBAL(0x00F44146, sint16) = start_z; - - RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, sint16) &= 0xFFF9; - RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, sint16) &= 0xFFF7; - - x = RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MAX, sint16) - - RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MIN, sint16); - - y = RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_Y_MAX, sint16) - - RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_Y_MIN, sint16); - - x /= 32; - y /= 32; - x++; - y++; - - track_design->space_required_x = (uint8)x; - track_design->space_required_y = (uint8)y; - - return 1; -} - -/** - * - * rct2: 0x006CE68D - */ -int tracked_ride_to_td6(uint8 rideIndex, rct_track_td6* track_design, uint8* track_elements) -{ - rct_ride* ride = get_ride(rideIndex); - rct_xy_element trackElement; - track_begin_end trackBeginEnd; - - if (!ride_try_get_origin_element(rideIndex, &trackElement)) { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - int z = 0; - // Find the start of the track. - // It has to do this as a backwards loop incase this is an incomplete track. - if (track_block_get_previous(trackElement.x, trackElement.y, trackElement.element, &trackBeginEnd)) { - rct_map_element* initial_map = trackElement.element; - do { - rct_xy_element lastGood = { - .element = trackBeginEnd.begin_element, - .x = trackBeginEnd.begin_x, - .y = trackBeginEnd.begin_y - }; - - if (!track_block_get_previous(trackBeginEnd.end_x, trackBeginEnd.end_y, trackBeginEnd.begin_element, &trackBeginEnd)) { - trackElement = lastGood; - break; - } - } while (initial_map != trackBeginEnd.begin_element); - } - - z = trackElement.element->base_height * 8; - uint8 track_type = trackElement.element->properties.track.type; - uint8 direction = trackElement.element->type & MAP_ELEMENT_DIRECTION_MASK; - RCT2_GLOBAL(0x00F4414D, uint8) = direction; - - if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)){ - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - const rct_track_coordinates *trackCoordinates = &TrackCoordinates[trackElement.element->properties.track.type]; - // Used in the following loop to know when we have - // completed all of the elements and are back at the - // start. - rct_map_element* initial_map = trackElement.element; - - sint16 start_x = trackElement.x; - sint16 start_y = trackElement.y; - sint16 start_z = z + trackCoordinates->z_begin; - RCT2_GLOBAL(0x00F44142, sint16) = start_x; - RCT2_GLOBAL(0x00F44144, sint16) = start_y; - RCT2_GLOBAL(0x00F44146, sint16) = start_z; - - rct_track_element* track = (rct_track_element*)track_elements; - do{ - track->type = trackElement.element->properties.track.type; - if (track->type == 0xFF) - track->type = 101; - - if (track->type == TRACK_ELEM_LEFT_VERTICAL_LOOP || - track->type == TRACK_ELEM_RIGHT_VERTICAL_LOOP) - track_design->flags |= (1 << 7); - - if (track->type == TRACK_ELEM_LEFT_TWIST_DOWN_TO_UP || - track->type == TRACK_ELEM_RIGHT_TWIST_DOWN_TO_UP || - track->type == TRACK_ELEM_LEFT_TWIST_UP_TO_DOWN || - track->type == TRACK_ELEM_RIGHT_TWIST_UP_TO_DOWN) - track_design->flags |= (1 << 17); - - if (track->type == TRACK_ELEM_LEFT_BARREL_ROLL_UP_TO_DOWN || - track->type == TRACK_ELEM_RIGHT_BARREL_ROLL_UP_TO_DOWN || - track->type == TRACK_ELEM_LEFT_BARREL_ROLL_DOWN_TO_UP || - track->type == TRACK_ELEM_RIGHT_BARREL_ROLL_DOWN_TO_UP) - track_design->flags |= (1 << 29); - - if (track->type == TRACK_ELEM_HALF_LOOP_UP || - track->type == TRACK_ELEM_HALF_LOOP_DOWN) - track_design->flags |= (1 << 18); - - if (track->type == TRACK_ELEM_LEFT_CORKSCREW_UP || - track->type == TRACK_ELEM_RIGHT_CORKSCREW_UP || - track->type == TRACK_ELEM_LEFT_CORKSCREW_DOWN || - track->type == TRACK_ELEM_RIGHT_CORKSCREW_DOWN) - track_design->flags |= (1 << 19); - - if (track->type == TRACK_ELEM_WATER_SPLASH) - track_design->flags |= (1 << 27); - - if (track->type == TRACK_ELEM_POWERED_LIFT) - track_design->flags |= (1 << 30); - - if (track->type == TRACK_ELEM_LEFT_LARGE_HALF_LOOP_UP || - track->type == TRACK_ELEM_RIGHT_LARGE_HALF_LOOP_UP || - track->type == TRACK_ELEM_RIGHT_LARGE_HALF_LOOP_DOWN || - track->type == TRACK_ELEM_LEFT_LARGE_HALF_LOOP_DOWN) - track_design->flags |= (1 << 31); - - if (track->type == TRACK_ELEM_LOG_FLUME_REVERSER) - track_design->flags2 |= TRACK_FLAGS2_CONTAINS_LOG_FLUME_REVERSER; - - uint8 bh; - if (track->type == TRACK_ELEM_BRAKES){ - bh = trackElement.element->properties.track.sequence >> 4; - } - else{ - bh = trackElement.element->properties.track.colour >> 4; - } - - uint8 flags = (trackElement.element->type & (1 << 7)) | bh; - flags |= (trackElement.element->properties.track.colour & 3) << 4; - - if ( - RideData4[ride->type].flags & RIDE_TYPE_FLAG4_3 && - trackElement.element->properties.track.colour & (1 << 2) - ) { - flags |= (1 << 6); - } - - track->flags = flags; - track++; - - if (!track_block_get_next(&trackElement, &trackElement, NULL, NULL)) - break; - - z = trackElement.element->base_height * 8; - direction = trackElement.element->type & MAP_ELEMENT_DIRECTION_MASK; - track_type = trackElement.element->properties.track.type; - - if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) - break; - - } while (trackElement.element != initial_map); - - track_elements = (uint8*)track; - // Mark the elements as finished. - *track_elements++ = 0xFF; - - rct_track_entrance* entrance = (rct_track_entrance*)track_elements; - - // First entrances, second exits - for (int i = 0; i < 2; ++i){ - - for (int station_index = 0; station_index < 4; ++station_index){ - - z = ride->station_heights[station_index]; - - uint16 location; - if (i == 0)location = ride->entrances[station_index]; - else location = ride->exits[station_index]; - - if (location == 0xFFFF) - continue; - - int x = (location & 0xFF) * 32; - int y = ((location & 0xFF00) >> 8) * 32; - - rct_map_element* map_element = map_get_first_element_at(x / 32, y / 32); - - do{ - if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_ENTRANCE) - continue; - if (map_element->base_height == z) - break; - } while (!map_element_is_last_for_tile(map_element++)); - // Add something that stops this from walking off the end - - uint8 entrance_direction = map_element->type & MAP_ELEMENT_DIRECTION_MASK; - entrance_direction -= RCT2_GLOBAL(0x00F4414D, uint8); - entrance_direction &= MAP_ELEMENT_DIRECTION_MASK; - - entrance->direction = entrance_direction; - - x -= RCT2_GLOBAL(0x00F44142, sint16); - y -= RCT2_GLOBAL(0x00F44144, sint16); - - switch (RCT2_GLOBAL(0x00F4414D, uint8)){ - case MAP_ELEMENT_DIRECTION_WEST: - // Nothing required - break; - case MAP_ELEMENT_DIRECTION_NORTH: - { - int temp_y = -y; - y = x; - x = temp_y; - } - break; - case MAP_ELEMENT_DIRECTION_EAST: - x = -x; - y = -y; - break; - case MAP_ELEMENT_DIRECTION_SOUTH: - { - int temp_x = -x; - x = y; - y = temp_x; - } - break; - } - - entrance->x = x; - entrance->y = y; - - z *= 8; - z -= RCT2_GLOBAL(0x00F44146, sint16); - z /= 8; - - if (z > 127 || z < -126){ - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - if (z == 0xFF) - z = 0x80; - - entrance->z = z; - - // If this is the exit version - if (i == 1){ - entrance->direction |= (1 << 7); - } - entrance++; - } - } - - track_elements = (uint8*)entrance; - *track_elements++ = 0xFF; - *track_elements++ = 0xFF; - - RCT2_GLOBAL(0x00F44058, uint8*) = track_elements; - - sub_6D01B3(track_design, PTD_OPERATION_DRAW_OUTLINES, 0, 4096, 4096, 0); - - // Resave global vars for scenery reasons. - RCT2_GLOBAL(0x00F44142, sint16) = start_x; - RCT2_GLOBAL(0x00F44144, sint16) = start_y; - RCT2_GLOBAL(0x00F44146, sint16) = start_z; - - RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, sint16) &= 0xFFF9; - RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, sint16) &= 0xFFF7; - - int x = RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MAX, sint16) - - RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MIN, sint16); - - int y = RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_Y_MAX, sint16) - - RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_Y_MIN, sint16); - - x /= 32; - y /= 32; - x++; - y++; - - track_design->space_required_x = x; - track_design->space_required_y = y; - - return 1; -} - -/** - * - * rct2: 0x006CE44F - */ -int ride_to_td6(uint8 rideIndex){ - rct_ride* ride = get_ride(rideIndex); - rct_track_td6* track_design = RCT2_ADDRESS(0x009D8178, rct_track_td6); - - track_design->type = ride->type; - rct_object_entry_extended* object = &object_entry_groups[OBJECT_TYPE_RIDE].entries[ride->subtype]; - - // Note we are only copying rct_object_entry in size and - // not the extended as we don't need the chunk size. - memcpy(&track_design->vehicle_object, object, sizeof(rct_object_entry)); - - track_design->ride_mode = ride->mode; - - track_design->version_and_colour_scheme = - (ride->colour_scheme_type & 3) | - (1 << 3); // Version .TD6 - - for (int i = 0; i < 32; ++i){ - track_design->vehicle_colours[i] = ride->vehicle_colours[i]; - track_design->vehicle_additional_colour[i] = ride->vehicle_colours_extended[i]; - } - - for (int i = 0; i < 4; ++i){ - track_design->track_spine_colour[i] = ride->track_colour_main[i]; - track_design->track_rail_colour[i] = ride->track_colour_additional[i]; - track_design->track_support_colour[i] = ride->track_colour_supports[i]; - } - - track_design->depart_flags = ride->depart_flags; - track_design->number_of_trains = ride->num_vehicles; - track_design->number_of_cars_per_train = ride->num_cars_per_train; - track_design->min_waiting_time = ride->min_waiting_time; - track_design->max_waiting_time = ride->max_waiting_time; - track_design->var_50 = ride->operation_option; - track_design->lift_hill_speed_num_circuits = - ride->lift_hill_speed | - (ride->num_circuits << 5); - - track_design->entrance_style = ride->entrance_style; - track_design->max_speed = (sint8)(ride->max_speed / 65536); - track_design->average_speed = (sint8)(ride->average_speed / 65536); - track_design->ride_length = ride_get_total_length(ride) / 65536; - track_design->max_positive_vertical_g = ride->max_positive_vertical_g / 32; - track_design->max_negative_vertical_g = ride->max_negative_vertical_g / 32; - track_design->max_lateral_g = ride->max_lateral_g / 32; - track_design->inversions = ride->inversions; - track_design->drops = ride->drops; - track_design->highest_drop_height = ride->highest_drop_height; - - uint16 total_air_time = (ride->total_air_time * 123) / 1024; - if (total_air_time > 255) - total_air_time = 0; - track_design->total_air_time = (uint8)total_air_time; - - track_design->excitement = ride->ratings.excitement / 10; - track_design->intensity = ride->ratings.intensity / 10; - track_design->nausea = ride->ratings.nausea / 10; - - track_design->upkeep_cost = ride->upkeep_cost; - track_design->flags = 0; - track_design->flags2 = 0; - - uint8* track_elements = RCT2_ADDRESS(0x9D821B, uint8); - memset(track_elements, 0, 8000); - - if (track_design->type == RIDE_TYPE_MAZE){ - return maze_ride_to_td6(rideIndex, track_design, track_elements); - } - - return tracked_ride_to_td6(rideIndex, track_design, track_elements); -} - -/** - * - * rct2: 0x006771DC but not really its branched from that - * quite far. - */ -int save_track_to_file(rct_track_td6* track_design, char* path) -{ - window_close_construction_windows(); - - uint8* track_file = malloc(0x8000); - - int length = sawyercoding_encode_td6((uint8 *)track_design, track_file, 0x609F); - - SDL_RWops *file; - - log_verbose("saving track %s", path); - file = SDL_RWFromFile(path, "wb"); - if (file == NULL) { - free(track_file); - log_error("Failed to save %s", path); - return 0; - } - - SDL_RWwrite(file, track_file, length, 1); - SDL_RWclose(file); - free(track_file); - - 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(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - return 0; - } - - if (ride->ratings.excitement == (ride_rating)0xFFFF){ - window_error_open(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - return 0; - } - - if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_TRACK)) { - window_error_open(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - return 0; - } - - if (!ride_to_td6(rideIndex)){ - window_error_open(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - 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]; - substitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_TRACKS_PATH, char), track_name); - - // Save track design - format_string(RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char), 2306, NULL); - - // Track design files - format_string(RCT2_ADDRESS(0x141EE68, char), 2305, NULL); - - // Show save dialog - utf8 initialDirectory[MAX_PATH]; - { - strcpy(initialDirectory, path); - utf8 *a = strrchr(initialDirectory, '/'); - utf8 *b = strrchr(initialDirectory, '\\'); - utf8 *c = max(a, b); - if (c != NULL) { - *c = '\0'; - } - } - - file_dialog_desc desc; - memset(&desc, 0, sizeof(desc)); - desc.type = FD_SAVE; - desc.title = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, utf8); - desc.initial_directory = initialDirectory; - desc.default_filename = path; - desc.filters[0].name = language_get_string(STR_OPENRCT2_TRACK_DESIGN_FILE); - desc.filters[0].pattern = "*.td6"; - - audio_pause_sounds(); - bool result = platform_open_common_file_dialog(path, &desc); - audio_unpause_sounds(); - - if (!result) { - ride_list_item item = { .type = 0xFD, .entry_index = 0 }; - track_load_list(item); - return 1; - } - - save_track_to_file(RCT2_ADDRESS(0x009D8178, rct_track_td6), path); - - ride_list_item item = { .type = 0xFC, .entry_index = 0 }; - track_load_list(item); - gfx_invalidate_screen(); - return 1; -} - /** * * rct2: 0x006D399D @@ -2783,11 +1898,11 @@ rct_track_design *temp_track_get_info(char* path, uint8** preview) log_verbose("Loading track: %s", path); - if (!(loaded_track = load_track_design(path))) { - if (preview != NULL) *preview = NULL; - log_error("Failed to load track: %s", path); - return NULL; - } + // if (!(loaded_track = load_track_design(path))) { + // if (preview != NULL) *preview = NULL; + // log_error("Failed to load track: %s", path); + // return NULL; + // } trackDesign = &RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_CACHE, rct_track_design*)[i]; @@ -2820,29 +1935,6 @@ rct_track_design *temp_track_get_info(char* path, uint8** preview) return trackDesign; } -void window_track_list_format_name(utf8 *dst, const utf8 *src, int colour, bool quotes) -{ - const utf8 *ch; - int codepoint; - char *lastDot = strrchr(src, '.'); - - if (colour != 0) { - dst = utf8_write_codepoint(dst, colour); - } - - if (quotes) dst = utf8_write_codepoint(dst, FORMAT_OPENQUOTES); - - ch = src; - while (lastDot > ch) { - codepoint = utf8_get_next(ch, &ch); - dst = utf8_write_codepoint(dst, codepoint); - } - - if (quotes) dst = utf8_write_codepoint(dst, FORMAT_ENDQUOTES); - - *dst = 0; -} - /** * * rct2: 0x006D40B2 @@ -2883,720 +1975,130 @@ int install_track(char* source_path, char* dest_name){ return platform_file_copy(source_path, dest_path, false); } -/** - * - * rct2: 0x006D3026 - */ -void track_save_reset_scenery() +money32 place_maze_design(uint8 flags, uint8 rideIndex, uint16 mazeEntry, sint16 x, sint16 y, sint16 z) { - RCT2_GLOBAL(0x009DA193, uint8) = 255; - gTrackSavedMapElements[0] = (rct_map_element*)0xFFFFFFFF; - gfx_invalidate_screen(); -} - -static bool track_save_contains_map_element(rct_map_element *mapElement) -{ - rct_map_element **savedMapElement; - - savedMapElement = gTrackSavedMapElements; - do { - if (*savedMapElement == mapElement) { - return true; - } - } while (*savedMapElement++ != (rct_map_element*)-1); - return false; -} - -static int map_element_get_total_element_count(rct_map_element *mapElement) -{ - int elementCount; - rct_scenery_entry *sceneryEntry; - rct_large_scenery_tile *tile; - - switch (map_element_get_type(mapElement)) { - case MAP_ELEMENT_TYPE_PATH: - case MAP_ELEMENT_TYPE_SCENERY: - case MAP_ELEMENT_TYPE_FENCE: - return 1; - - case MAP_ELEMENT_TYPE_SCENERY_MULTIPLE: - sceneryEntry = g_largeSceneryEntries[mapElement->properties.scenerymultiple.type & 0x3FF]; - tile = sceneryEntry->large_scenery.tiles; - elementCount = 0; - do { - tile++; - elementCount++; - } while (tile->x_offset != (sint16)0xFFFF); - return elementCount; - - default: - return 0; - } -} - -static bool track_scenery_is_null(rct_track_scenery *trackScenery) -{ - return *((uint8*)trackScenery) == 0xFF; -} - -static void track_scenery_set_to_null(rct_track_scenery *trackScenery) -{ - *((uint8*)trackScenery) = 0xFF; -} - -static rct_map_element **track_get_next_spare_saved_map_element() -{ - rct_map_element **savedMapElement = gTrackSavedMapElements; - while (*savedMapElement != (rct_map_element*)0xFFFFFFFF) { - savedMapElement++; - } - return savedMapElement; -} - -/** - * - * rct2: 0x006D2ED2 - */ -static bool track_save_can_add_map_element(rct_map_element *mapElement) -{ - int newElementCount = map_element_get_total_element_count(mapElement); - if (newElementCount == 0) { - return false; + gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; + gCommandPosition.x = x + 8; + gCommandPosition.y = y + 8; + gCommandPosition.z = z; + if (!sub_68B044()) { + return MONEY32_UNDEFINED; } - // Get number of saved elements so far - rct_map_element **savedMapElement = track_get_next_spare_saved_map_element(); - - // Get number of spare elements left - int numSavedElements = savedMapElement - gTrackSavedMapElements; - int spareSavedElements = TRACK_MAX_SAVED_MAP_ELEMENTS - numSavedElements; - if (newElementCount > spareSavedElements) { - // No more spare saved elements left - return false; + if ((z & 15) != 0) { + return MONEY32_UNDEFINED; } - // Probably checking for spare elements in the TD6 struct - rct_track_scenery *trackScenery = (rct_track_scenery*)0x009DA193; - while (!track_scenery_is_null(trackScenery)) { trackScenery++; } - if (trackScenery >= (rct_track_scenery*)0x9DE207) { - return false; - } - - return true; -} - -/** - * - * rct2: 0x006D2F4C - */ -static void track_save_push_map_element(int x, int y, rct_map_element *mapElement) -{ - rct_map_element **savedMapElement; - - map_invalidate_tile_full(x, y); - savedMapElement = track_get_next_spare_saved_map_element(); - *savedMapElement = mapElement; - *(savedMapElement + 1) = (rct_map_element*)0xFFFFFFFF; -} - -/** - * - * rct2: 0x006D2FA7 - */ -static void track_save_push_map_element_desc(rct_object_entry *entry, int x, int y, int z, uint8 flags, uint8 primaryColour, uint8 secondaryColour) -{ - rct_track_scenery *item = (rct_track_scenery*)0x009DA193; - while (!track_scenery_is_null(item)) { item++; } - - item->scenery_object = *entry; - item->x = x / 32; - item->y = y / 32; - item->z = z; - item->flags = flags; - item->primary_colour = primaryColour; - item->secondary_colour = secondaryColour; - - track_scenery_set_to_null(item + 1); -} - -static void track_save_add_scenery(int x, int y, rct_map_element *mapElement) -{ - int entryType = mapElement->properties.scenery.type; - rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_SMALL_SCENERY].entries[entryType]; - - uint8 flags = 0; - flags |= mapElement->type & 3; - flags |= (mapElement->type & 0xC0) >> 4; - - uint8 primaryColour = mapElement->properties.scenery.colour_1 & 0x1F; - uint8 secondaryColour = mapElement->properties.scenery.colour_2 & 0x1F; - - track_save_push_map_element(x, y, mapElement); - track_save_push_map_element_desc(entry, x, y, mapElement->base_height, flags, primaryColour, secondaryColour); -} - -static void track_save_add_large_scenery(int x, int y, rct_map_element *mapElement) -{ - rct_large_scenery_tile *sceneryTiles, *tile; - int x0, y0, z0, z; - int direction, sequence; - - int entryType = mapElement->properties.scenerymultiple.type & 0x3FF; - rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_LARGE_SCENERY].entries[entryType]; - sceneryTiles = g_largeSceneryEntries[entryType]->large_scenery.tiles; - - z = mapElement->base_height; - direction = mapElement->type & 3; - sequence = mapElement->properties.scenerymultiple.type >> 10; - - if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, NULL)) { - return; - } - - // Iterate through each tile of the large scenery element - sequence = 0; - for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) { - sint16 offsetX = tile->x_offset; - sint16 offsetY = tile->y_offset; - rotate_map_coordinates(&offsetX, &offsetY, direction); - - x = x0 + offsetX; - y = y0 + offsetY; - z = (z0 + tile->z_offset) / 8; - mapElement = map_get_large_scenery_segment(x, y, z, direction, sequence); - if (mapElement != NULL) { - if (sequence == 0) { - uint8 flags = mapElement->type & 3; - uint8 primaryColour = mapElement->properties.scenerymultiple.colour[0] & 0x1F; - uint8 secondaryColour = mapElement->properties.scenerymultiple.colour[1] & 0x1F; - - track_save_push_map_element_desc(entry, x, y, z, flags, primaryColour, secondaryColour); - } - track_save_push_map_element(x, y, mapElement); + if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED)) { + if (game_is_paused()) { + gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; + return MONEY32_UNDEFINED; } } -} -static void track_save_add_wall(int x, int y, rct_map_element *mapElement) -{ - int entryType = mapElement->properties.fence.type; - rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_WALLS].entries[entryType]; - - uint8 flags = 0; - flags |= mapElement->type & 3; - flags |= mapElement->properties.fence.item[0] << 2; - - uint8 secondaryColour = ((mapElement->flags & 0x60) >> 2) | (mapElement->properties.fence.item[1] >> 5); - uint8 primaryColour = mapElement->properties.fence.item[1] & 0x1F; - - track_save_push_map_element(x, y, mapElement); - track_save_push_map_element_desc(entry, x, y, mapElement->base_height, flags, primaryColour, secondaryColour); -} - -static void track_save_add_footpath(int x, int y, rct_map_element *mapElement) -{ - int entryType = mapElement->properties.path.type >> 4; - rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_PATHS].entries[entryType]; - - uint8 flags = 0; - flags |= mapElement->properties.path.edges & 0x0F; - flags |= (mapElement->properties.path.type & 4) << 2; - flags |= (mapElement->properties.path.type & 3) << 5; - flags |= (mapElement->type & 1) << 7; - - track_save_push_map_element(x, y, mapElement); - track_save_push_map_element_desc(entry, x, y, mapElement->base_height, flags, 0, 0); -} - -/** - * - * rct2: 0x006D2B3C - */ -static bool track_save_add_map_element(int interactionType, int x, int y, rct_map_element *mapElement) -{ - if (!track_save_can_add_map_element(mapElement)) { - return false; - } - - switch (interactionType) { - case VIEWPORT_INTERACTION_ITEM_SCENERY: - track_save_add_scenery(x, y, mapElement); - return true; - case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: - track_save_add_large_scenery(x, y, mapElement); - return true; - case VIEWPORT_INTERACTION_ITEM_WALL: - track_save_add_wall(x, y, mapElement); - return true; - case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - track_save_add_footpath(x, y, mapElement); - return true; - default: - return false; - } -} - -/** - * - * rct2: 0x006D2F78 - */ -static void track_save_pop_map_element(int x, int y, rct_map_element *mapElement) -{ - map_invalidate_tile_full(x, y); - - // Find map element and total of saved elements - int removeIndex = -1; - int numSavedElements = 0; - rct_map_element **savedMapElement = gTrackSavedMapElements; - while (*savedMapElement != (rct_map_element*)0xFFFFFFFF) { - if (*savedMapElement == mapElement) { - removeIndex = numSavedElements; - } - savedMapElement++; - numSavedElements++; - } - - if (removeIndex == -1) { - return; - } - - // Remove item and shift rest up one item - if (removeIndex < numSavedElements - 1) { - memmove(&gTrackSavedMapElements[removeIndex], &gTrackSavedMapElements[removeIndex + 1], (numSavedElements - removeIndex - 1) * sizeof(rct_map_element*)); - } - gTrackSavedMapElements[numSavedElements - 1] = (rct_map_element*)0xFFFFFFFF; -} - -/** - * - * rct2: 0x006D2FDD - */ -static void track_save_pop_map_element_desc(rct_object_entry *entry, int x, int y, int z, uint8 flags, uint8 primaryColour, uint8 secondaryColour) -{ - int removeIndex = -1; - int totalItems = 0; - - rct_track_scenery *items = (rct_track_scenery*)0x009DA193; - rct_track_scenery *item = items; - for (; !track_scenery_is_null(item); item++, totalItems++) { - if (item->x != x / 32) continue; - if (item->y != y / 32) continue; - if (item->z != z) continue; - if (item->flags != flags) continue; - if (!object_entry_compare(&item->scenery_object, entry)) continue; - - removeIndex = totalItems; - } - - if (removeIndex == -1) { - return; - } - - // Remove item and shift rest up one item - if (removeIndex < totalItems - 1) { - memmove(&items[removeIndex], &items[removeIndex + 1], (totalItems - removeIndex - 1) * sizeof(rct_track_scenery)); - } - track_scenery_set_to_null(&items[totalItems - 1]); -} - -static void track_save_remove_scenery(int x, int y, rct_map_element *mapElement) -{ - int entryType = mapElement->properties.scenery.type; - rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_SMALL_SCENERY].entries[entryType]; - - uint8 flags = 0; - flags |= mapElement->type & 3; - flags |= (mapElement->type & 0xC0) >> 4; - - uint8 primaryColour = mapElement->properties.scenery.colour_1 & 0x1F; - uint8 secondaryColour = mapElement->properties.scenery.colour_2 & 0x1F; - - track_save_pop_map_element(x, y, mapElement); - track_save_pop_map_element_desc(entry, x, y, mapElement->base_height, flags, primaryColour, secondaryColour); -} - -static void track_save_remove_large_scenery(int x, int y, rct_map_element *mapElement) -{ - rct_large_scenery_tile *sceneryTiles, *tile; - int x0, y0, z0, z; - int direction, sequence; - - int entryType = mapElement->properties.scenerymultiple.type & 0x3FF; - rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_LARGE_SCENERY].entries[entryType]; - sceneryTiles = g_largeSceneryEntries[entryType]->large_scenery.tiles; - - z = mapElement->base_height; - direction = mapElement->type & 3; - sequence = mapElement->properties.scenerymultiple.type >> 10; - - if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, NULL)) { - return; - } - - // Iterate through each tile of the large scenery element - sequence = 0; - for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) { - sint16 offsetX = tile->x_offset; - sint16 offsetY = tile->y_offset; - rotate_map_coordinates(&offsetX, &offsetY, direction); - - x = x0 + offsetX; - y = y0 + offsetY; - z = (z0 + tile->z_offset) / 8; - mapElement = map_get_large_scenery_segment(x, y, z, direction, sequence); - if (mapElement != NULL) { - if (sequence == 0) { - uint8 flags = mapElement->type & 3; - uint8 primaryColour = mapElement->properties.scenerymultiple.colour[0] & 0x1F; - uint8 secondaryColour = mapElement->properties.scenerymultiple.colour[1] & 0x1F; - - track_save_pop_map_element_desc(entry, x, y, z, flags, primaryColour, secondaryColour); - } - track_save_pop_map_element(x, y, mapElement); + if (flags & GAME_COMMAND_FLAG_APPLY) { + if (!(flags & GAME_COMMAND_FLAG_GHOST)) { + footpath_remove_litter(x, y, z); + map_remove_walls_at(floor2(x, 32), floor2(y, 32), z, z + 32); } } -} -static void track_save_remove_wall(int x, int y, rct_map_element *mapElement) -{ - int entryType = mapElement->properties.fence.type; - rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_WALLS].entries[entryType]; - - uint8 flags = 0; - flags |= mapElement->type & 3; - flags |= mapElement->properties.fence.item[0] << 2; - - uint8 secondaryColour = ((mapElement->flags & 0x60) >> 2) | (mapElement->properties.fence.item[1] >> 5); - uint8 primaryColour = mapElement->properties.fence.item[1] & 0x1F; - - track_save_pop_map_element(x, y, mapElement); - track_save_pop_map_element_desc(entry, x, y, mapElement->base_height, flags, primaryColour, secondaryColour); -} - -static void track_save_remove_footpath(int x, int y, rct_map_element *mapElement) -{ - int entryType = mapElement->properties.path.type >> 4; - rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_PATHS].entries[entryType]; - - uint8 flags = 0; - flags |= mapElement->properties.path.edges & 0x0F; - flags |= (mapElement->properties.path.type & 4) << 2; - flags |= (mapElement->properties.path.type & 3) << 5; - flags |= (mapElement->type & 1) << 7; - - track_save_pop_map_element(x, y, mapElement); - track_save_pop_map_element_desc(entry, x, y, mapElement->base_height, flags, 0, 0); -} - -/** - * - * rct2: 0x006D2B3C - */ -static void track_save_remove_map_element(int interactionType, int x, int y, rct_map_element *mapElement) -{ - switch (interactionType) { - case VIEWPORT_INTERACTION_ITEM_SCENERY: - track_save_remove_scenery(x, y, mapElement); - break; - case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: - track_save_remove_large_scenery(x, y, mapElement); - break; - case VIEWPORT_INTERACTION_ITEM_WALL: - track_save_remove_wall(x, y, mapElement); - break; - case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - track_save_remove_footpath(x, y, mapElement); - break; - } -} - -/** - * - * rct2: 0x006D2B07 - */ -void track_save_toggle_map_element(int interactionType, int x, int y, rct_map_element *mapElement) -{ - if (track_save_contains_map_element(mapElement)) { - track_save_remove_map_element(interactionType, x, y, mapElement); - } else { - if (!track_save_add_map_element(interactionType, x, y, mapElement)) { - window_error_open( - STR_SAVE_TRACK_SCENERY_UNABLE_TO_SELECT_ADDITIONAL_ITEM_OF_SCENERY, - STR_SAVE_TRACK_SCENERY_TOO_MANY_ITEMS_SELECTED - ); + if (!gCheatsSandboxMode) { + if (!map_is_location_owned(floor2(x, 32), floor2(y, 32), z)) { + return MONEY32_UNDEFINED; } } -} -/** - * - * rct2: 0x006D303D - */ -void track_save_select_nearby_scenery(int rideIndex) -{ - rct_map_element *mapElement; - - for (int y = 0; y < 256; y++) { - for (int x = 0; x < 256; x++) { - mapElement = map_get_first_element_at(x, y); - do { - if (track_save_should_select_scenery_around(rideIndex, mapElement)) { - track_save_select_nearby_scenery_for_tile(rideIndex, x, y); - break; - } - } while (!map_element_is_last_for_tile(mapElement++)); - } - } - gfx_invalidate_screen(); -} - -static bool track_save_should_select_scenery_around(int rideIndex, rct_map_element *mapElement) -{ - switch (map_element_get_type(mapElement)) { - case MAP_ELEMENT_TYPE_PATH: - if ((mapElement->type & 1) && mapElement->properties.path.addition_status == rideIndex) - return true; - break; - case MAP_ELEMENT_TYPE_TRACK: - if (mapElement->properties.track.ride_index == rideIndex) - return true; - break; - case MAP_ELEMENT_TYPE_ENTRANCE: - if (mapElement->properties.entrance.type != ENTRANCE_TYPE_RIDE_ENTRANCE) - break; - if (mapElement->properties.entrance.type != ENTRANCE_TYPE_RIDE_EXIT) - break; - if (mapElement->properties.entrance.ride_index == rideIndex) - return true; - break; - } - return false; -} - -static void track_save_select_nearby_scenery_for_tile(int rideIndex, int cx, int cy) -{ - rct_map_element *mapElement; - - for (int y = cy - 1; y <= cy + 1; y++) { - for (int x = cx - 1; x <= cx + 1; x++) { - mapElement = map_get_first_element_at(x, y); - do { - int interactionType = VIEWPORT_INTERACTION_ITEM_NONE; - switch (map_element_get_type(mapElement)) { - case MAP_ELEMENT_TYPE_PATH: - if (!(mapElement->type & 1)) - interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; - else if (mapElement->properties.path.addition_status == rideIndex) - interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; - break; - case MAP_ELEMENT_TYPE_SCENERY: - interactionType = VIEWPORT_INTERACTION_ITEM_SCENERY; - break; - case MAP_ELEMENT_TYPE_FENCE: - interactionType = VIEWPORT_INTERACTION_ITEM_WALL; - break; - case MAP_ELEMENT_TYPE_SCENERY_MULTIPLE: - interactionType = VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY; - break; - } - - if (interactionType != VIEWPORT_INTERACTION_ITEM_NONE) { - if (!track_save_contains_map_element(mapElement)) { - track_save_add_map_element(interactionType, x * 32, y * 32, mapElement); - } - } - } while (!map_element_is_last_for_tile(mapElement++)); - } - } -} - -bool track_design_open(rct_track_td6 *td6, const utf8 *path) -{ - SDL_RWops *file = SDL_RWFromFile(path, "rb"); - if (file != NULL) { - // Read whole file into a buffer - size_t bufferLength = (size_t)SDL_RWsize(file); - uint8 *buffer = (uint8*)malloc(bufferLength); - if (buffer == NULL) { - log_error("Unable to allocate memory for track design file."); - SDL_RWclose(file); - return false; - } - SDL_RWread(file, buffer, bufferLength, 1); - SDL_RWclose(file); - - if (!sawyercoding_validate_track_checksum(buffer, bufferLength)) { - log_error("Track checksum failed."); - free(buffer); - return false; - } - - // Decode the track data - uint8 *decoded = malloc(0x10000); - size_t decodedLength = sawyercoding_decode_td6(buffer, decoded, bufferLength); - free(buffer); - decoded = realloc(decoded, decodedLength); - if (decoded == NULL) { - log_error("failed to realloc"); - } else { - track_design_open_from_buffer(td6, decoded, decodedLength); - free(decoded); - return true; - } - } - return false; -} - -static bool track_design_open_from_buffer(rct_track_td6 *td6, uint8 *src, size_t srcLength) -{ - uint8 *readPtr = src; - - // Clear top of track_design as this is not loaded from the td4 files - memset(&td6->track_spine_colour, 0, 67); - - // Read start of track_design - copy(td6, &readPtr, 32); - - uint8 version = td6->version_and_colour_scheme >> 2; - if (version > 2) { - log_error("Unsupported track design."); - return false; - } - - // In TD6 there are 32 sets of two byte vehicle colour specifiers - // In TD4 there are 12 sets so the remaining 20 need to be read - if (version == 2) { - copy(&td6->vehicle_colours[12], &readPtr, 40); - } - - copy(&td6->pad_48, &readPtr, 24); - - // In TD4 (version AA/CF) and TD6 both start actual track data at 0xA3 - if (version > 0) { - copy(&td6->track_spine_colour, &readPtr, version == 1 ? 140 : 67); - } - - // Read the actual track data to memory directly after the passed in TD6 struct - size_t elementDataLength = srcLength - (readPtr - src); - uint8 *elementData = malloc(elementDataLength); - if (elementData == NULL) { - log_error("Unable to allocate memory for TD6 element data."); - return false; - } - copy(elementData, &readPtr, elementDataLength); - td6->elements = elementData; - td6->elementsSize = elementDataLength; - - uint8 *final_track_element_location = elementData + elementDataLength; - - // TD4 files require some extra work to be recognised as TD6. - if (version < 2) { - // Set any element passed the tracks to 0xFF - if (td6->type == RIDE_TYPE_MAZE) { - rct_maze_element* maze_element = (rct_maze_element*)elementData; - while (maze_element->all != 0) { - maze_element++; - } - maze_element++; - memset(maze_element, 255, final_track_element_location - (uint8*)maze_element); - } else { - rct_track_element* track_element = (rct_track_element*)elementData; - while (track_element->type != 255) { - track_element++; - } - memset(((uint8*)track_element) + 1, 255, final_track_element_location - (uint8*)track_element); - } - - // Convert the colours from RCT1 to RCT2 - for (int i = 0; i < 32; i++) { - rct_vehicle_colour *vehicleColour = &td6->vehicle_colours[i]; - vehicleColour->body_colour = rct1_get_colour(vehicleColour->body_colour); - vehicleColour->trim_colour = rct1_get_colour(vehicleColour->trim_colour); - } - - td6->track_spine_colour_rct1 = rct1_get_colour(td6->track_spine_colour_rct1); - td6->track_rail_colour_rct1 = rct1_get_colour(td6->track_rail_colour_rct1); - td6->track_support_colour_rct1 = rct1_get_colour(td6->track_support_colour_rct1); - - for (int i = 0; i < 4; i++) { - td6->track_spine_colour[i] = rct1_get_colour(td6->track_spine_colour[i]); - td6->track_rail_colour[i] = rct1_get_colour(td6->track_rail_colour[i]); - td6->track_support_colour[i] = rct1_get_colour(td6->track_support_colour[i]); - } - - // Highest drop height is 1bit = 3/4 a meter in TD6 - // Highest drop height is 1bit = 1/3 a meter in TD4 - // Not sure if this is correct?? - td6->highest_drop_height >>= 1; - - // If it has boosters then sadly track has to be discarded. - if (td4_track_has_boosters(td6, elementData)) { - log_error("Track design contains RCT1 boosters which are not yet supported."); - free(td6->elements); - td6->elements = NULL; - return false; - } - - // Convert RCT1 ride type to RCT2 ride type - uint8 rct1RideType = td6->type; - if (rct1RideType == RCT1_RIDE_TYPE_WOODEN_ROLLER_COASTER) { - td6->type = RIDE_TYPE_WOODEN_ROLLER_COASTER; - } else if (rct1RideType == RCT1_RIDE_TYPE_STEEL_CORKSCREW_ROLLER_COASTER) { - if (td6->vehicle_type == RCT1_VEHICLE_TYPE_HYPERCOASTER_TRAIN) { - if (td6->ride_mode == RCT1_RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE) { - td6->ride_mode = RIDE_MODE_CONTINUOUS_CIRCUIT; - } + // Check support height + if (!gCheatsDisableSupportLimits) { + rct_map_element *mapElement = map_get_surface_element_at(x >> 5, y >> 5); + uint8 supportZ = (z + 32) >> 3; + if (supportZ > mapElement->base_height) { + uint8 supportHeight = (supportZ - mapElement->base_height) / 2; + uint8 maxSupportHeight = RideData5[RIDE_TYPE_MAZE].max_height; + if (supportHeight > maxSupportHeight) { + gGameCommandErrorText = STR_TOO_HIGH_FOR_SUPPORTS; + return MONEY32_UNDEFINED; } } - - // All TD4s that use powered launch use the type that doesn't pass the station. - if (td6->ride_mode == RCT1_RIDE_MODE_POWERED_LAUNCH) { - td6->ride_mode = RIDE_MODE_POWERED_LAUNCH; - } - - // Convert RCT1 vehicle type to RCT2 vehicle type - rct_object_entry *vehicle_object; - if (td6->type == RIDE_TYPE_MAZE) { - vehicle_object = RCT2_ADDRESS(0x0097F66C, rct_object_entry); - } else { - int vehicle_type = td6->vehicle_type; - if (vehicle_type == RCT1_VEHICLE_TYPE_INVERTED_COASTER_TRAIN && - td6->type == RIDE_TYPE_INVERTED_ROLLER_COASTER - ) { - vehicle_type = RCT1_VEHICLE_TYPE_4_ACROSS_INVERTED_COASTER_TRAIN; - } - vehicle_object = &RCT2_ADDRESS(0x0097F0DC, rct_object_entry)[vehicle_type]; - } - memcpy(&td6->vehicle_object, vehicle_object, sizeof(rct_object_entry)); - - // Further vehicle colour fixes - for (int i = 0; i < 32; i++) { - td6->vehicle_additional_colour[i] = td6->vehicle_colours[i].trim_colour; - - // RCT1 river rapids always had black seats. - if (rct1RideType == RCT1_RIDE_TYPE_RIVER_RAPIDS) { - td6->vehicle_colours[i].trim_colour = COLOUR_BLACK; - } - } - - td6->space_required_x = 255; - td6->space_required_y = 255; - td6->lift_hill_speed_num_circuits = 5; } - td6->var_50 = min( - td6->var_50, - RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + 5 + (td6->type * 8), uint8) - ); + money32 cost = 0; + // Clearance checks + if (!gCheatsDisableClearanceChecks) { + int fx = floor2(x, 32); + int fy = floor2(y, 32); + int fz0 = z >> 3; + int fz1 = fz0 + 4; - return true; + if (!map_can_construct_with_clear_at(fx, fy, fz0, fz1, &map_place_non_scenery_clear_func, 15, flags, &cost)) { + return MONEY32_UNDEFINED; + } + + uint8 elctgaw = RCT2_GLOBAL(RCT2_ADDRESS_ELEMENT_LOCATION_COMPARED_TO_GROUND_AND_WATER, uint8); + if (elctgaw & ELEMENT_IS_UNDERWATER) { + gGameCommandErrorText = STR_RIDE_CANT_BUILD_THIS_UNDERWATER; + return MONEY32_UNDEFINED; + } + if (elctgaw & ELEMENT_IS_UNDERGROUND) { + gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND; + return MONEY32_UNDEFINED; + } + } + + rct_ride *ride = get_ride(rideIndex); + + // Calculate price + money32 price = 0; + if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) { + price = RideTrackCosts[ride->type].track_price * RCT2_GLOBAL(0x0099DBC8, money32); + price = (price >> 17) * 10; + } + + cost += price; + + if (flags & GAME_COMMAND_FLAG_APPLY) { + if (RCT2_GLOBAL(0x009A8C28, uint8) == 1 && !(flags & GAME_COMMAND_FLAG_GHOST)) { + rct_xyz16 coord; + coord.x = x + 8; + coord.y = y + 8; + coord.z = z; + network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); + } + + // Place track element + int fx = floor2(x, 32); + int fy = floor2(y, 32); + int fz = z >> 3; + rct_map_element *mapElement = map_element_insert(fx >> 5, fy >> 5, fz, 15); + mapElement->clearance_height = fz + 4; + mapElement->type = MAP_ELEMENT_TYPE_TRACK; + mapElement->properties.track.type = 101; + mapElement->properties.track.ride_index = rideIndex; + mapElement->properties.track.maze_entry = mazeEntry; + if (flags & GAME_COMMAND_FLAG_GHOST) { + mapElement->flags |= MAP_ELEMENT_FLAG_GHOST; + } + + map_invalidate_element(fx, fy, mapElement); + + ride->maze_tiles++; + ride->station_heights[0] = mapElement->base_height; + ride->station_starts[0] = 0; + if (ride->maze_tiles == 1) { + ride->overall_view = (fx >> 5) | ((fy >> 5) << 8); + } + } + + return cost; } /** * * rct2: 0x006D13FE */ -void game_command_place_track_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp){ +void game_command_place_track_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) +{ int x = *eax; int y = *ecx; int z = *edi; @@ -3744,124 +2246,6 @@ void game_command_place_track_design(int* eax, int* ebx, int* ecx, int* edx, int *edi = rideIndex; } -money32 place_maze_design(uint8 flags, uint8 rideIndex, uint16 mazeEntry, sint16 x, sint16 y, sint16 z) -{ - gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; - gCommandPosition.x = x + 8; - gCommandPosition.y = y + 8; - gCommandPosition.z = z; - if (!sub_68B044()) { - return MONEY32_UNDEFINED; - } - - if ((z & 15) != 0) { - return MONEY32_UNDEFINED; - } - - if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED)) { - if (game_is_paused()) { - gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED; - return MONEY32_UNDEFINED; - } - } - - if (flags & GAME_COMMAND_FLAG_APPLY) { - if (!(flags & GAME_COMMAND_FLAG_GHOST)) { - footpath_remove_litter(x, y, z); - map_remove_walls_at(floor2(x, 32), floor2(y, 32), z, z + 32); - } - } - - if (!gCheatsSandboxMode) { - if (!map_is_location_owned(floor2(x, 32), floor2(y, 32), z)) { - return MONEY32_UNDEFINED; - } - } - - // Check support height - if (!gCheatsDisableSupportLimits) { - rct_map_element *mapElement = map_get_surface_element_at(x >> 5, y >> 5); - uint8 supportZ = (z + 32) >> 3; - if (supportZ > mapElement->base_height) { - uint8 supportHeight = (supportZ - mapElement->base_height) / 2; - uint8 maxSupportHeight = RideData5[RIDE_TYPE_MAZE].max_height; - if (supportHeight > maxSupportHeight) { - gGameCommandErrorText = STR_TOO_HIGH_FOR_SUPPORTS; - return MONEY32_UNDEFINED; - } - } - } - - money32 cost = 0; - // Clearance checks - if (!gCheatsDisableClearanceChecks) { - int fx = floor2(x, 32); - int fy = floor2(y, 32); - int fz0 = z >> 3; - int fz1 = fz0 + 4; - - if (!map_can_construct_with_clear_at(fx, fy, fz0, fz1, &map_place_non_scenery_clear_func, 15, flags, &cost)) { - return MONEY32_UNDEFINED; - } - - uint8 elctgaw = RCT2_GLOBAL(RCT2_ADDRESS_ELEMENT_LOCATION_COMPARED_TO_GROUND_AND_WATER, uint8); - if (elctgaw & ELEMENT_IS_UNDERWATER) { - gGameCommandErrorText = STR_RIDE_CANT_BUILD_THIS_UNDERWATER; - return MONEY32_UNDEFINED; - } - if (elctgaw & ELEMENT_IS_UNDERGROUND) { - gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ABOVE_GROUND; - return MONEY32_UNDEFINED; - } - } - - rct_ride *ride = get_ride(rideIndex); - - // Calculate price - money32 price = 0; - if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) { - price = RideTrackCosts[ride->type].track_price * RCT2_GLOBAL(0x0099DBC8, money32); - price = (price >> 17) * 10; - } - - cost += price; - - if (flags & GAME_COMMAND_FLAG_APPLY) { - if (RCT2_GLOBAL(0x009A8C28, uint8) == 1 && !(flags & GAME_COMMAND_FLAG_GHOST)) { - rct_xyz16 coord; - coord.x = x + 8; - coord.y = y + 8; - coord.z = z; - network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); - } - - // Place track element - int fx = floor2(x, 32); - int fy = floor2(y, 32); - int fz = z >> 3; - rct_map_element *mapElement = map_element_insert(fx >> 5, fy >> 5, fz, 15); - mapElement->clearance_height = fz + 4; - mapElement->type = MAP_ELEMENT_TYPE_TRACK; - mapElement->properties.track.type = 101; - mapElement->properties.track.ride_index = rideIndex; - mapElement->properties.track.maze_entry = mazeEntry; - if (flags & GAME_COMMAND_FLAG_GHOST) { - mapElement->flags |= MAP_ELEMENT_FLAG_GHOST; - } - - map_invalidate_element(fx, fy, mapElement); - - ride->maze_tiles++; - ride->station_heights[0] = mapElement->base_height; - ride->station_starts[0] = 0; - if (ride->maze_tiles == 1) { - ride->overall_view = (fx >> 5) | ((fy >> 5) << 8); - } - } - - return cost; -} - /** * * rct2: 0x006CDEE4 @@ -3878,3 +2262,97 @@ void game_command_place_maze_design(int* eax, int* ebx, int* ecx, int* edx, int* ); } +#pragma region Track Design Preview + +/** + * Create a backup of the map as it will be cleared for drawing the track + * design preview. + * rct2: 0x006D1C68 + */ +static bool track_design_preview_backup_map() +{ + RCT2_GLOBAL(0xF440ED, uint8*) = malloc(0xED600); + if (RCT2_GLOBAL(0xF440ED, uint32) == 0) { + return false; + } + + RCT2_GLOBAL(0xF440F1, uint8*) = malloc(0x40000); + if (RCT2_GLOBAL(0xF440F1, uint32) == 0){ + free(RCT2_GLOBAL(0xF440ED, uint8*)); + return false; + } + + RCT2_GLOBAL(0xF440F5, uint8*) = malloc(14); + if (RCT2_GLOBAL(0xF440F5, uint32) == 0){ + free(RCT2_GLOBAL(0xF440ED, uint8*)); + free(RCT2_GLOBAL(0xF440F1, uint8*)); + return false; + } + + uint32 *mapElements = RCT2_ADDRESS(RCT2_ADDRESS_MAP_ELEMENTS, uint32); + memcpy(RCT2_GLOBAL(0xF440ED, uint32*), mapElements, 0xED600); + + uint32 *tilePointers = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, uint32); + memcpy(RCT2_GLOBAL(0xF440F1, uint32*), tilePointers, 0x40000); + + uint8* backup_info = RCT2_GLOBAL(0xF440F5, uint8*); + *(uint32*)backup_info = (uint32)gNextFreeMapElement; + *(uint16*)(backup_info + 4) = gMapSizeUnits; + *(uint16*)(backup_info + 6) = gMapSizeMinus2; + *(uint16*)(backup_info + 8) = gMapSize; + *(uint32*)(backup_info + 10) = get_current_rotation(); + return true; +} + +/** + * Restores the map from a backup. + * rct2: 0x006D2378 + */ +static void track_design_preview_restore_map() +{ + uint32* map_elements = RCT2_ADDRESS(RCT2_ADDRESS_MAP_ELEMENTS, uint32); + memcpy(map_elements, RCT2_GLOBAL(0xF440ED, uint32*), 0xED600); + + uint32* tile_map_pointers = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, uint32); + memcpy(tile_map_pointers, RCT2_GLOBAL(0xF440F1, uint32*), 0x40000); + + uint8* backup_info = RCT2_GLOBAL(0xF440F5, uint8*); + gNextFreeMapElement = (rct_map_element*)backup_info; + gMapSizeUnits = *(uint16*)(backup_info + 4); + gMapSizeMinus2 = *(uint16*)(backup_info + 6); + gMapSize = *(uint16*)(backup_info + 8); + gCurrentRotation = *(uint8*)(backup_info + 10); + + free(RCT2_GLOBAL(0xF440ED, uint8*)); + free(RCT2_GLOBAL(0xF440F1, uint8*)); + free(RCT2_GLOBAL(0xF440F5, uint8*)); +} + +/** + * Resets all the map elements to surface tiles for track preview. + * rct2: 0x006D1D9A + */ +static void track_design_preview_clear_map() +{ + // These values were previously allocated in backup map but + // it seems more fitting to place in this function + gMapSizeUnits = 0x1FE0; + gMapSizeMinus2 = 0x20FE; + gMapSize = 0x100; + + rct_map_element* map_element; + for (int i = 0; i < MAX_TILE_MAP_ELEMENT_POINTERS; i++) { + map_element = GET_MAP_ELEMENT(i); + map_element->type = MAP_ELEMENT_TYPE_SURFACE; + map_element->flags = MAP_ELEMENT_FLAG_LAST_TILE; + map_element->base_height = 2; + map_element->clearance_height = 0; + map_element->properties.surface.slope = 0; + map_element->properties.surface.terrain = 0; + map_element->properties.surface.grass_length = 1; + map_element->properties.surface.ownership = OWNERSHIP_OWNED; + } + map_update_tile_pointers(); +} + +#pragma endregion diff --git a/src/ride/track_design.h b/src/ride/track_design.h index 4165a775b2..8323254ab3 100644 --- a/src/ride/track_design.h +++ b/src/ride/track_design.h @@ -111,26 +111,17 @@ extern rct_track_td6 *gActiveTrackDesign; extern money32 gTrackDesignCost; extern uint8 gTrackDesignPlaceFlags; -void track_load_list(ride_list_item item); -int sub_67726A(const char *path); -rct_track_design *track_get_info(int index, uint8** preview); +// void track_load_list(ride_list_item item); +// rct_track_design *track_get_info(int index, uint8** preview); rct_track_design *temp_track_get_info(char* path, uint8** preview); -rct_track_td6* load_track_design(const char *path); -int track_rename(const char *text); +// int track_rename(const char *text); int track_delete(); void track_mirror(); -void reset_track_list_cache(); bool track_design_open(rct_track_td6 *td6, const utf8 *path); void draw_track_preview(rct_track_td6 *td6, uint8** preview); int sub_6D01B3(rct_track_td6 *td6, uint8 bl, uint8 rideIndex, int x, int y, int z); -int save_track_design(uint8 rideIndex); int install_track(char* source_path, char* dest_name); -void window_track_list_format_name(utf8 *dst, const utf8 *src, int colour, bool quotes); - -void track_save_reset_scenery(); -void track_save_select_nearby_scenery(int rideIndex); -void track_save_toggle_map_element(int interactionType, int x, int y, rct_map_element *mapElement); void track_design_index_create(); size_t track_design_index_get_for_ride(track_design_file_ref **tdRefs, uint8 rideType, const char *entry); @@ -139,4 +130,12 @@ utf8 *track_design_get_name_from_path(const utf8 *path); void game_command_place_track_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); void game_command_place_maze_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp); +/////////////////////////////////////////////////////////////////////////////// +// Track design saving +/////////////////////////////////////////////////////////////////////////////// +void track_design_save_reset_scenery(); +void track_design_save_select_nearby_scenery(int rideIndex); +void track_design_save_toggle_map_element(int interactionType, int x, int y, rct_map_element *mapElement); +bool track_design_save(uint8 rideIndex); + #endif diff --git a/src/ride/track_design_save.c b/src/ride/track_design_save.c new file mode 100644 index 0000000000..03f4881019 --- /dev/null +++ b/src/ride/track_design_save.c @@ -0,0 +1,1224 @@ +#include "../audio/audio.h" +#include "../game.h" +#include "../localisation/localisation.h" +#include "../localisation/string_ids.h" +#include "../interface/viewport.h" +#include "../util/sawyercoding.h" +#include "../windows/error.h" +#include "../world/scenery.h" +#include "ride_data.h" +#include "track.h" +#include "track_data.h" +#include "track_design.h" + +#define TRACK_MAX_SAVED_MAP_ELEMENTS 1500 + +static bool track_design_save_should_select_scenery_around(int rideIndex, rct_map_element *mapElement); +static void track_design_save_select_nearby_scenery_for_tile(int rideIndex, int cx, int cy); +static bool track_design_save_contains_map_element(rct_map_element *mapElement); +static bool track_design_save_add_map_element(int interactionType, int x, int y, rct_map_element *mapElement); +static void track_design_save_remove_map_element(int interactionType, int x, int y, rct_map_element *mapElement); +static bool track_design_save_copy_scenery_to_track(uint8 **track_pointer); +static bool track_design_save_to_td6(rct_track_td6 *td6, uint8 rideIndex); +static bool track_design_save_to_td6_for_maze(uint8 rideIndex, rct_track_td6 *td6, uint8 *trackElements); +static bool track_design_save_to_td6_for_tracked_ride(uint8 rideIndex, rct_track_td6 *td6, uint8 *trackElements); +static bool track_design_save_to_file(rct_track_td6 *track_design, utf8 *path); + +/** + * + * rct2: 0x006D2B07 + */ +void track_design_save_toggle_map_element(int interactionType, int x, int y, rct_map_element *mapElement) +{ + if (track_design_save_contains_map_element(mapElement)) { + track_design_save_remove_map_element(interactionType, x, y, mapElement); + } else { + if (!track_design_save_add_map_element(interactionType, x, y, mapElement)) { + window_error_open( + STR_SAVE_TRACK_SCENERY_UNABLE_TO_SELECT_ADDITIONAL_ITEM_OF_SCENERY, + STR_SAVE_TRACK_SCENERY_TOO_MANY_ITEMS_SELECTED + ); + } + } +} + +/** + * + * rct2: 0x006D303D + */ +void track_design_save_select_nearby_scenery(int rideIndex) +{ + rct_map_element *mapElement; + + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + mapElement = map_get_first_element_at(x, y); + do { + if (track_design_save_should_select_scenery_around(rideIndex, mapElement)) { + track_design_save_select_nearby_scenery_for_tile(rideIndex, x, y); + break; + } + } while (!map_element_is_last_for_tile(mapElement++)); + } + } + gfx_invalidate_screen(); +} + +/** + * + * rct2: 0x006D3026 + */ +void track_design_save_reset_scenery() +{ + RCT2_GLOBAL(0x009DA193, uint8) = 255; + gTrackSavedMapElements[0] = (rct_map_element*)0xFFFFFFFF; + gfx_invalidate_screen(); +} + +/** + * + * rct2: 0x006D2804, 0x006D264D + */ +bool track_design_save(uint8 rideIndex) +{ + rct_ride* ride = get_ride(rideIndex); + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)){ + window_error_open(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); + return false; + } + + if (ride->ratings.excitement == (ride_rating)0xFFFF){ + window_error_open(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); + return false; + } + + if (!ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_TRACK)) { + window_error_open(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); + return false; + } + + rct_track_td6 *td6 = RCT2_ADDRESS(0x009D8178, rct_track_td6); + if (!track_design_save_to_td6(td6, rideIndex)) { + window_error_open(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); + return false; + } + + uint8 *trackPointer = RCT2_GLOBAL(0x00F44058, uint8*); + if (RCT2_GLOBAL(0x009DEA6F, uint8) & 1) { + if (!track_design_save_copy_scenery_to_track(&trackPointer)) { + return false; + } + } + + while (trackPointer < RCT2_ADDRESS(0x009DE217, uint8))*trackPointer++ = 0; + + char track_name[MAX_PATH]; + // Get track name + format_string(track_name, ride->name, &ride->name_arguments); + + char path[MAX_PATH]; + substitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_TRACKS_PATH, char), track_name); + + // Save track design + format_string(RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, char), 2306, NULL); + + // Track design files + format_string(RCT2_ADDRESS(0x141EE68, char), 2305, NULL); + + // Show save dialog + utf8 initialDirectory[MAX_PATH]; + { + strcpy(initialDirectory, path); + utf8 *a = strrchr(initialDirectory, '/'); + utf8 *b = strrchr(initialDirectory, '\\'); + utf8 *c = max(a, b); + if (c != NULL) { + *c = '\0'; + } + } + + file_dialog_desc desc; + memset(&desc, 0, sizeof(desc)); + desc.type = FD_SAVE; + desc.title = RCT2_ADDRESS(RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER, utf8); + desc.initial_directory = initialDirectory; + desc.default_filename = path; + desc.filters[0].name = language_get_string(STR_OPENRCT2_TRACK_DESIGN_FILE); + desc.filters[0].pattern = "*.td6"; + + audio_pause_sounds(); + bool result = platform_open_common_file_dialog(path, &desc); + audio_unpause_sounds(); + + if (!result) { + ride_list_item item = { .type = 0xFD, .entry_index = 0 }; + // track_load_list(item); + return true; + } + + track_design_save_to_file(RCT2_ADDRESS(0x009D8178, rct_track_td6), path); + + ride_list_item item = { .type = 0xFC, .entry_index = 0 }; + // track_load_list(item); + gfx_invalidate_screen(); + return true; +} + +static bool track_design_save_contains_map_element(rct_map_element *mapElement) +{ + rct_map_element **savedMapElement; + + savedMapElement = gTrackSavedMapElements; + do { + if (*savedMapElement == mapElement) { + return true; + } + } while (*savedMapElement++ != (rct_map_element*)-1); + return false; +} + +static int map_element_get_total_element_count(rct_map_element *mapElement) +{ + int elementCount; + rct_scenery_entry *sceneryEntry; + rct_large_scenery_tile *tile; + + switch (map_element_get_type(mapElement)) { + case MAP_ELEMENT_TYPE_PATH: + case MAP_ELEMENT_TYPE_SCENERY: + case MAP_ELEMENT_TYPE_FENCE: + return 1; + + case MAP_ELEMENT_TYPE_SCENERY_MULTIPLE: + sceneryEntry = g_largeSceneryEntries[mapElement->properties.scenerymultiple.type & 0x3FF]; + tile = sceneryEntry->large_scenery.tiles; + elementCount = 0; + do { + tile++; + elementCount++; + } while (tile->x_offset != (sint16)0xFFFF); + return elementCount; + + default: + return 0; + } +} + +static bool track_design_save_scenery_is_null(rct_track_scenery *trackScenery) +{ + return *((uint8*)trackScenery) == 0xFF; +} + +static void track_design_save_scenery_set_to_null(rct_track_scenery *trackScenery) +{ + *((uint8*)trackScenery) = 0xFF; +} + +static rct_map_element **track_design_save_get_next_spare_map_element() +{ + rct_map_element **savedMapElement = gTrackSavedMapElements; + while (*savedMapElement != (rct_map_element*)0xFFFFFFFF) { + savedMapElement++; + } + return savedMapElement; +} + +/** + * + * rct2: 0x006D2ED2 + */ +static bool track_design_save_can_add_map_element(rct_map_element *mapElement) +{ + int newElementCount = map_element_get_total_element_count(mapElement); + if (newElementCount == 0) { + return false; + } + + // Get number of saved elements so far + rct_map_element **savedMapElement = track_design_save_get_next_spare_map_element(); + + // Get number of spare elements left + int numSavedElements = savedMapElement - gTrackSavedMapElements; + int spareSavedElements = TRACK_MAX_SAVED_MAP_ELEMENTS - numSavedElements; + if (newElementCount > spareSavedElements) { + // No more spare saved elements left + return false; + } + + // Probably checking for spare elements in the TD6 struct + rct_track_scenery *trackScenery = (rct_track_scenery*)0x009DA193; + while (!track_design_save_scenery_is_null(trackScenery)) { trackScenery++; } + if (trackScenery >= (rct_track_scenery*)0x9DE207) { + return false; + } + + return true; +} + +/** + * + * rct2: 0x006D2F4C + */ +static void track_design_save_push_map_element(int x, int y, rct_map_element *mapElement) +{ + rct_map_element **savedMapElement; + + map_invalidate_tile_full(x, y); + savedMapElement = track_design_save_get_next_spare_map_element(); + *savedMapElement = mapElement; + *(savedMapElement + 1) = (rct_map_element*)0xFFFFFFFF; +} + +/** + * + * rct2: 0x006D2FA7 + */ +static void track_design_save_push_map_element_desc(rct_object_entry *entry, int x, int y, int z, uint8 flags, uint8 primaryColour, uint8 secondaryColour) +{ + rct_track_scenery *item = (rct_track_scenery*)0x009DA193; + while (!track_design_save_scenery_is_null(item)) { item++; } + + item->scenery_object = *entry; + item->x = x / 32; + item->y = y / 32; + item->z = z; + item->flags = flags; + item->primary_colour = primaryColour; + item->secondary_colour = secondaryColour; + + track_design_save_scenery_set_to_null(item + 1); +} + +static void track_design_save_add_scenery(int x, int y, rct_map_element *mapElement) +{ + int entryType = mapElement->properties.scenery.type; + rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_SMALL_SCENERY].entries[entryType]; + + uint8 flags = 0; + flags |= mapElement->type & 3; + flags |= (mapElement->type & 0xC0) >> 4; + + uint8 primaryColour = mapElement->properties.scenery.colour_1 & 0x1F; + uint8 secondaryColour = mapElement->properties.scenery.colour_2 & 0x1F; + + track_design_save_push_map_element(x, y, mapElement); + track_design_save_push_map_element_desc(entry, x, y, mapElement->base_height, flags, primaryColour, secondaryColour); +} + +static void track_design_save_add_large_scenery(int x, int y, rct_map_element *mapElement) +{ + rct_large_scenery_tile *sceneryTiles, *tile; + int x0, y0, z0, z; + int direction, sequence; + + int entryType = mapElement->properties.scenerymultiple.type & 0x3FF; + rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_LARGE_SCENERY].entries[entryType]; + sceneryTiles = g_largeSceneryEntries[entryType]->large_scenery.tiles; + + z = mapElement->base_height; + direction = mapElement->type & 3; + sequence = mapElement->properties.scenerymultiple.type >> 10; + + if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, NULL)) { + return; + } + + // Iterate through each tile of the large scenery element + sequence = 0; + for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) { + sint16 offsetX = tile->x_offset; + sint16 offsetY = tile->y_offset; + rotate_map_coordinates(&offsetX, &offsetY, direction); + + x = x0 + offsetX; + y = y0 + offsetY; + z = (z0 + tile->z_offset) / 8; + mapElement = map_get_large_scenery_segment(x, y, z, direction, sequence); + if (mapElement != NULL) { + if (sequence == 0) { + uint8 flags = mapElement->type & 3; + uint8 primaryColour = mapElement->properties.scenerymultiple.colour[0] & 0x1F; + uint8 secondaryColour = mapElement->properties.scenerymultiple.colour[1] & 0x1F; + + track_design_save_push_map_element_desc(entry, x, y, z, flags, primaryColour, secondaryColour); + } + track_design_save_push_map_element(x, y, mapElement); + } + } +} + +static void track_design_save_add_wall(int x, int y, rct_map_element *mapElement) +{ + int entryType = mapElement->properties.fence.type; + rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_WALLS].entries[entryType]; + + uint8 flags = 0; + flags |= mapElement->type & 3; + flags |= mapElement->properties.fence.item[0] << 2; + + uint8 secondaryColour = ((mapElement->flags & 0x60) >> 2) | (mapElement->properties.fence.item[1] >> 5); + uint8 primaryColour = mapElement->properties.fence.item[1] & 0x1F; + + track_design_save_push_map_element(x, y, mapElement); + track_design_save_push_map_element_desc(entry, x, y, mapElement->base_height, flags, primaryColour, secondaryColour); +} + +static void track_design_save_add_footpath(int x, int y, rct_map_element *mapElement) +{ + int entryType = mapElement->properties.path.type >> 4; + rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_PATHS].entries[entryType]; + + uint8 flags = 0; + flags |= mapElement->properties.path.edges & 0x0F; + flags |= (mapElement->properties.path.type & 4) << 2; + flags |= (mapElement->properties.path.type & 3) << 5; + flags |= (mapElement->type & 1) << 7; + + track_design_save_push_map_element(x, y, mapElement); + track_design_save_push_map_element_desc(entry, x, y, mapElement->base_height, flags, 0, 0); +} + +/** + * + * rct2: 0x006D2B3C + */ +static bool track_design_save_add_map_element(int interactionType, int x, int y, rct_map_element *mapElement) +{ + if (!track_design_save_can_add_map_element(mapElement)) { + return false; + } + + switch (interactionType) { + case VIEWPORT_INTERACTION_ITEM_SCENERY: + track_design_save_add_scenery(x, y, mapElement); + return true; + case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: + track_design_save_add_large_scenery(x, y, mapElement); + return true; + case VIEWPORT_INTERACTION_ITEM_WALL: + track_design_save_add_wall(x, y, mapElement); + return true; + case VIEWPORT_INTERACTION_ITEM_FOOTPATH: + track_design_save_add_footpath(x, y, mapElement); + return true; + default: + return false; + } +} + +/** + * + * rct2: 0x006D2F78 + */ +static void track_design_save_pop_map_element(int x, int y, rct_map_element *mapElement) +{ + map_invalidate_tile_full(x, y); + + // Find map element and total of saved elements + int removeIndex = -1; + int numSavedElements = 0; + rct_map_element **savedMapElement = gTrackSavedMapElements; + while (*savedMapElement != (rct_map_element*)0xFFFFFFFF) { + if (*savedMapElement == mapElement) { + removeIndex = numSavedElements; + } + savedMapElement++; + numSavedElements++; + } + + if (removeIndex == -1) { + return; + } + + // Remove item and shift rest up one item + if (removeIndex < numSavedElements - 1) { + memmove(&gTrackSavedMapElements[removeIndex], &gTrackSavedMapElements[removeIndex + 1], (numSavedElements - removeIndex - 1) * sizeof(rct_map_element*)); + } + gTrackSavedMapElements[numSavedElements - 1] = (rct_map_element*)0xFFFFFFFF; +} + +/** + * + * rct2: 0x006D2FDD + */ +static void track_design_save_pop_map_element_desc(rct_object_entry *entry, int x, int y, int z, uint8 flags, uint8 primaryColour, uint8 secondaryColour) +{ + int removeIndex = -1; + int totalItems = 0; + + rct_track_scenery *items = (rct_track_scenery*)0x009DA193; + rct_track_scenery *item = items; + for (; !track_design_save_scenery_is_null(item); item++, totalItems++) { + if (item->x != x / 32) continue; + if (item->y != y / 32) continue; + if (item->z != z) continue; + if (item->flags != flags) continue; + if (!object_entry_compare(&item->scenery_object, entry)) continue; + + removeIndex = totalItems; + } + + if (removeIndex == -1) { + return; + } + + // Remove item and shift rest up one item + if (removeIndex < totalItems - 1) { + memmove(&items[removeIndex], &items[removeIndex + 1], (totalItems - removeIndex - 1) * sizeof(rct_track_scenery)); + } + track_design_save_scenery_set_to_null(&items[totalItems - 1]); +} + +static void track_design_save_remove_scenery(int x, int y, rct_map_element *mapElement) +{ + int entryType = mapElement->properties.scenery.type; + rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_SMALL_SCENERY].entries[entryType]; + + uint8 flags = 0; + flags |= mapElement->type & 3; + flags |= (mapElement->type & 0xC0) >> 4; + + uint8 primaryColour = mapElement->properties.scenery.colour_1 & 0x1F; + uint8 secondaryColour = mapElement->properties.scenery.colour_2 & 0x1F; + + track_design_save_pop_map_element(x, y, mapElement); + track_design_save_pop_map_element_desc(entry, x, y, mapElement->base_height, flags, primaryColour, secondaryColour); +} + +static void track_design_save_remove_large_scenery(int x, int y, rct_map_element *mapElement) +{ + rct_large_scenery_tile *sceneryTiles, *tile; + int x0, y0, z0, z; + int direction, sequence; + + int entryType = mapElement->properties.scenerymultiple.type & 0x3FF; + rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_LARGE_SCENERY].entries[entryType]; + sceneryTiles = g_largeSceneryEntries[entryType]->large_scenery.tiles; + + z = mapElement->base_height; + direction = mapElement->type & 3; + sequence = mapElement->properties.scenerymultiple.type >> 10; + + if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, NULL)) { + return; + } + + // Iterate through each tile of the large scenery element + sequence = 0; + for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) { + sint16 offsetX = tile->x_offset; + sint16 offsetY = tile->y_offset; + rotate_map_coordinates(&offsetX, &offsetY, direction); + + x = x0 + offsetX; + y = y0 + offsetY; + z = (z0 + tile->z_offset) / 8; + mapElement = map_get_large_scenery_segment(x, y, z, direction, sequence); + if (mapElement != NULL) { + if (sequence == 0) { + uint8 flags = mapElement->type & 3; + uint8 primaryColour = mapElement->properties.scenerymultiple.colour[0] & 0x1F; + uint8 secondaryColour = mapElement->properties.scenerymultiple.colour[1] & 0x1F; + + track_design_save_pop_map_element_desc(entry, x, y, z, flags, primaryColour, secondaryColour); + } + track_design_save_pop_map_element(x, y, mapElement); + } + } +} + +static void track_design_save_remove_wall(int x, int y, rct_map_element *mapElement) +{ + int entryType = mapElement->properties.fence.type; + rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_WALLS].entries[entryType]; + + uint8 flags = 0; + flags |= mapElement->type & 3; + flags |= mapElement->properties.fence.item[0] << 2; + + uint8 secondaryColour = ((mapElement->flags & 0x60) >> 2) | (mapElement->properties.fence.item[1] >> 5); + uint8 primaryColour = mapElement->properties.fence.item[1] & 0x1F; + + track_design_save_pop_map_element(x, y, mapElement); + track_design_save_pop_map_element_desc(entry, x, y, mapElement->base_height, flags, primaryColour, secondaryColour); +} + +static void track_design_save_remove_footpath(int x, int y, rct_map_element *mapElement) +{ + int entryType = mapElement->properties.path.type >> 4; + rct_object_entry *entry = (rct_object_entry*)&object_entry_groups[OBJECT_TYPE_PATHS].entries[entryType]; + + uint8 flags = 0; + flags |= mapElement->properties.path.edges & 0x0F; + flags |= (mapElement->properties.path.type & 4) << 2; + flags |= (mapElement->properties.path.type & 3) << 5; + flags |= (mapElement->type & 1) << 7; + + track_design_save_pop_map_element(x, y, mapElement); + track_design_save_pop_map_element_desc(entry, x, y, mapElement->base_height, flags, 0, 0); +} + +/** + * + * rct2: 0x006D2B3C + */ +static void track_design_save_remove_map_element(int interactionType, int x, int y, rct_map_element *mapElement) +{ + switch (interactionType) { + case VIEWPORT_INTERACTION_ITEM_SCENERY: + track_design_save_remove_scenery(x, y, mapElement); + break; + case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: + track_design_save_remove_large_scenery(x, y, mapElement); + break; + case VIEWPORT_INTERACTION_ITEM_WALL: + track_design_save_remove_wall(x, y, mapElement); + break; + case VIEWPORT_INTERACTION_ITEM_FOOTPATH: + track_design_save_remove_footpath(x, y, mapElement); + break; + } +} + +static bool track_design_save_should_select_scenery_around(int rideIndex, rct_map_element *mapElement) +{ + switch (map_element_get_type(mapElement)) { + case MAP_ELEMENT_TYPE_PATH: + if ((mapElement->type & 1) && mapElement->properties.path.addition_status == rideIndex) + return true; + break; + case MAP_ELEMENT_TYPE_TRACK: + if (mapElement->properties.track.ride_index == rideIndex) + return true; + break; + case MAP_ELEMENT_TYPE_ENTRANCE: + if (mapElement->properties.entrance.type != ENTRANCE_TYPE_RIDE_ENTRANCE) + break; + if (mapElement->properties.entrance.type != ENTRANCE_TYPE_RIDE_EXIT) + break; + if (mapElement->properties.entrance.ride_index == rideIndex) + return true; + break; + } + return false; +} + +static void track_design_save_select_nearby_scenery_for_tile(int rideIndex, int cx, int cy) +{ + rct_map_element *mapElement; + + for (int y = cy - 1; y <= cy + 1; y++) { + for (int x = cx - 1; x <= cx + 1; x++) { + mapElement = map_get_first_element_at(x, y); + do { + int interactionType = VIEWPORT_INTERACTION_ITEM_NONE; + switch (map_element_get_type(mapElement)) { + case MAP_ELEMENT_TYPE_PATH: + if (!(mapElement->type & 1)) + interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; + else if (mapElement->properties.path.addition_status == rideIndex) + interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; + break; + case MAP_ELEMENT_TYPE_SCENERY: + interactionType = VIEWPORT_INTERACTION_ITEM_SCENERY; + break; + case MAP_ELEMENT_TYPE_FENCE: + interactionType = VIEWPORT_INTERACTION_ITEM_WALL; + break; + case MAP_ELEMENT_TYPE_SCENERY_MULTIPLE: + interactionType = VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY; + break; + } + + if (interactionType != VIEWPORT_INTERACTION_ITEM_NONE) { + if (!track_design_save_contains_map_element(mapElement)) { + track_design_save_add_map_element(interactionType, x * 32, y * 32, mapElement); + } + } + } while (!map_element_is_last_for_tile(mapElement++)); + } + } +} + +/* Based on rct2: 0x006D2897 */ +static bool track_design_save_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 (true) { + 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); + } + + sint16 x = ((uint8)track_scenery->x) * 32 - RCT2_GLOBAL(0x00F44142, sint16); + sint16 y = ((uint8)track_scenery->y) * 32 - RCT2_GLOBAL(0x00F44144, sint16); + rotate_map_coordinates(&x, &y, RCT2_GLOBAL(0x00F4414D, uint8)); + x /= 32; + y /= 32; + + if (x > 127 || y > 127 || x < -126 || y < -126){ + window_error_open(3346, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); + return false; + } + + track_scenery->x = (sint8)x; + track_scenery->y = (sint8)y; + + int z = track_scenery->z * 8 - RCT2_GLOBAL(0xF44146, sint16); + z /= 8; + if (z > 127 || z < -126){ + window_error_open(3346, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); + return false; + } + + track_scenery->z = z; + + track_scenery++; + scenery_source++; + } + + *track_pointer = (uint8*)track_scenery; + //Skip end of scenery elements byte + (*track_pointer)++; + return true; +} + +/** + * + * rct2: 0x006CE44F + */ +static bool track_design_save_to_td6(rct_track_td6 *td6, uint8 rideIndex) +{ + rct_ride *ride = get_ride(rideIndex); + td6->type = ride->type; + rct_object_entry_extended *object = &object_entry_groups[OBJECT_TYPE_RIDE].entries[ride->subtype]; + + // Note we are only copying rct_object_entry in size and + // not the extended as we don't need the chunk size. + memcpy(&td6->vehicle_object, object, sizeof(rct_object_entry)); + + td6->ride_mode = ride->mode; + + td6->version_and_colour_scheme = + (ride->colour_scheme_type & 3) | + (1 << 3); // Version .TD6 + + for (int i = 0; i < 32; i++) { + td6->vehicle_colours[i] = ride->vehicle_colours[i]; + td6->vehicle_additional_colour[i] = ride->vehicle_colours_extended[i]; + } + + for (int i = 0; i < 4; i++) { + td6->track_spine_colour[i] = ride->track_colour_main[i]; + td6->track_rail_colour[i] = ride->track_colour_additional[i]; + td6->track_support_colour[i] = ride->track_colour_supports[i]; + } + + td6->depart_flags = ride->depart_flags; + td6->number_of_trains = ride->num_vehicles; + td6->number_of_cars_per_train = ride->num_cars_per_train; + td6->min_waiting_time = ride->min_waiting_time; + td6->max_waiting_time = ride->max_waiting_time; + td6->var_50 = ride->operation_option; + td6->lift_hill_speed_num_circuits = + ride->lift_hill_speed | + (ride->num_circuits << 5); + + td6->entrance_style = ride->entrance_style; + td6->max_speed = (sint8)(ride->max_speed / 65536); + td6->average_speed = (sint8)(ride->average_speed / 65536); + td6->ride_length = ride_get_total_length(ride) / 65536; + td6->max_positive_vertical_g = ride->max_positive_vertical_g / 32; + td6->max_negative_vertical_g = ride->max_negative_vertical_g / 32; + td6->max_lateral_g = ride->max_lateral_g / 32; + td6->inversions = ride->inversions; + td6->drops = ride->drops; + td6->highest_drop_height = ride->highest_drop_height; + + uint16 total_air_time = (ride->total_air_time * 123) / 1024; + if (total_air_time > 255) { + total_air_time = 0; + } + td6->total_air_time = (uint8)total_air_time; + + td6->excitement = ride->ratings.excitement / 10; + td6->intensity = ride->ratings.intensity / 10; + td6->nausea = ride->ratings.nausea / 10; + + td6->upkeep_cost = ride->upkeep_cost; + td6->flags = 0; + td6->flags2 = 0; + + uint8 *trackElements = RCT2_ADDRESS(0x9D821B, uint8); + memset(trackElements, 0, 8000); + + if (td6->type == RIDE_TYPE_MAZE) { + return track_design_save_to_td6_for_maze(rideIndex, td6, trackElements); + } else { + return track_design_save_to_td6_for_tracked_ride(rideIndex, td6, trackElements); + } +} + +/** + * + * rct2: 0x006CEAAE + */ +static bool track_design_save_to_td6_for_maze(uint8 rideIndex, rct_track_td6 *td6, uint8 *trackElements) +{ + rct_map_element *mapElement = NULL; + bool mapFound = false; + sint16 startX, startY; + for (startY = 0; startY < 8192; startY += 32) { + for (startX = 0; startX < 8192; startX += 32) { + mapElement = map_get_first_element_at(startX >> 5, startY >> 5); + do { + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) + continue; + if (mapElement->properties.track.ride_index == rideIndex){ + mapFound = true; + break; + } + } while (!map_element_is_last_for_tile(mapElement++)); + if (mapFound) { + break; + } + } + if (mapFound) { + break; + } + } + + if (mapFound == 0) { + gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + return false; + } + + RCT2_GLOBAL(0x00F44142, sint16) = startX; + RCT2_GLOBAL(0x00F44144, sint16) = startY; + RCT2_GLOBAL(0x00F44146, sint16) = mapElement->base_height * 8; + + rct_maze_element *maze = (rct_maze_element*)trackElements; + + // x is defined here as we can start the search + // on tile start_x, start_y but then the next row + // must restart on 0 + for (sint16 y = startY, x = startX; y < 8192; y += 32) { + for (; x < 8192; x += 32) { + mapElement = map_get_first_element_at(x / 32, y / 32); + do { + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) continue; + if (mapElement->properties.track.ride_index != rideIndex) continue; + + maze->maze_entry = mapElement->properties.track.maze_entry; + maze->x = (x - startX) / 32; + maze->y = (y - startY) / 32; + maze++; + + if (maze >= RCT2_ADDRESS(0x009DA151, rct_maze_element)) { + gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + return false; + } + } while (!map_element_is_last_for_tile(mapElement++)); + + } + x = 0; + } + + rct_ride *ride = get_ride(rideIndex); + uint16 location = ride->entrances[0]; + + if (location == 0xFFFF) { + gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + return false; + } + + sint16 x = (location & 0xFF) * 32; + sint16 y = ((location & 0xFF00) >> 8) * 32; + + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_ENTRANCE) continue; + if (mapElement->properties.entrance.type != ENTRANCE_TYPE_RIDE_ENTRANCE) continue; + if (mapElement->properties.entrance.ride_index == rideIndex) break; + } while (!map_element_is_last_for_tile(mapElement++)); + // Add something that stops this from walking off the end + + uint8 entrance_direction = mapElement->type & MAP_ELEMENT_DIRECTION_MASK; + maze->unk_2 = entrance_direction; + maze->type = 8; + maze->x = (sint8)((x - startX) / 32); + maze->y = (sint8)((y - startY) / 32); + maze++; + + location = ride->exits[0]; + if (location == 0xFFFF) { + gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + return 0; + } + + x = (location & 0xFF) * 32; + y = ((location & 0xFF00) >> 8) * 32; + mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_ENTRANCE) continue; + if (mapElement->properties.entrance.type != ENTRANCE_TYPE_RIDE_EXIT) continue; + if (mapElement->properties.entrance.ride_index == rideIndex) break; + } while (!map_element_is_last_for_tile(mapElement++)); + // Add something that stops this from walking off the end + + uint8 exit_direction = mapElement->type & MAP_ELEMENT_DIRECTION_MASK; + maze->unk_2 = exit_direction; + maze->type = 0x80; + maze->x = (sint8)((x - startX) / 32); + maze->y = (sint8)((y - startY) / 32); + maze++; + maze->all = 0; + maze++; + + trackElements = (uint8*)maze; + *trackElements++ = 0xFF; + + RCT2_GLOBAL(0x00F44058, uint8*) = trackElements; + + // Save global vars as they are still used by scenery + sint16 start_z = RCT2_GLOBAL(0x00F44146, sint16); + sub_6D01B3(td6, PTD_OPERATION_DRAW_OUTLINES, 0, 4096, 4096, 0); + RCT2_GLOBAL(0x00F44142, sint16) = startX; + RCT2_GLOBAL(0x00F44144, sint16) = startY; + RCT2_GLOBAL(0x00F44146, sint16) = start_z; + + RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, sint16) &= 0xFFF9; + RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, sint16) &= 0xFFF7; + + x = RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MAX, sint16) - + RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MIN, sint16); + + y = RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_Y_MAX, sint16) - + RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_Y_MIN, sint16); + + x /= 32; + y /= 32; + x++; + y++; + + td6->space_required_x = (uint8)x; + td6->space_required_y = (uint8)y; + return true; +} + +/** + * + * rct2: 0x006CE68D + */ +static bool track_design_save_to_td6_for_tracked_ride(uint8 rideIndex, rct_track_td6 *td6, uint8 *trackElements) +{ + rct_ride *ride = get_ride(rideIndex); + rct_xy_element trackElement; + track_begin_end trackBeginEnd; + + if (!ride_try_get_origin_element(rideIndex, &trackElement)) { + gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + return false; + } + + int z = 0; + // Find the start of the track. + // It has to do this as a backwards loop incase this is an incomplete track. + if (track_block_get_previous(trackElement.x, trackElement.y, trackElement.element, &trackBeginEnd)) { + rct_map_element* initial_map = trackElement.element; + do { + rct_xy_element lastGood = { + .element = trackBeginEnd.begin_element, + .x = trackBeginEnd.begin_x, + .y = trackBeginEnd.begin_y + }; + + if (!track_block_get_previous(trackBeginEnd.end_x, trackBeginEnd.end_y, trackBeginEnd.begin_element, &trackBeginEnd)) { + trackElement = lastGood; + break; + } + } while (initial_map != trackBeginEnd.begin_element); + } + + z = trackElement.element->base_height * 8; + uint8 track_type = trackElement.element->properties.track.type; + uint8 direction = trackElement.element->type & MAP_ELEMENT_DIRECTION_MASK; + RCT2_GLOBAL(0x00F4414D, uint8) = direction; + + if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) { + gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + return 0; + } + + const rct_track_coordinates *trackCoordinates = &TrackCoordinates[trackElement.element->properties.track.type]; + // Used in the following loop to know when we have + // completed all of the elements and are back at the + // start. + rct_map_element *initialMap = trackElement.element; + + sint16 start_x = trackElement.x; + sint16 start_y = trackElement.y; + sint16 start_z = z + trackCoordinates->z_begin; + RCT2_GLOBAL(0x00F44142, sint16) = start_x; + RCT2_GLOBAL(0x00F44144, sint16) = start_y; + RCT2_GLOBAL(0x00F44146, sint16) = start_z; + + rct_track_element *track = (rct_track_element*)trackElements; + do{ + track->type = trackElement.element->properties.track.type; + if (track->type == 0xFF) { + track->type = 101; + } + + if (track->type == TRACK_ELEM_LEFT_VERTICAL_LOOP || + track->type == TRACK_ELEM_RIGHT_VERTICAL_LOOP + ) { + td6->flags |= (1 << 7); + } + + if (track->type == TRACK_ELEM_LEFT_TWIST_DOWN_TO_UP || + track->type == TRACK_ELEM_RIGHT_TWIST_DOWN_TO_UP || + track->type == TRACK_ELEM_LEFT_TWIST_UP_TO_DOWN || + track->type == TRACK_ELEM_RIGHT_TWIST_UP_TO_DOWN + ) { + td6->flags |= (1 << 17); + } + + if (track->type == TRACK_ELEM_LEFT_BARREL_ROLL_UP_TO_DOWN || + track->type == TRACK_ELEM_RIGHT_BARREL_ROLL_UP_TO_DOWN || + track->type == TRACK_ELEM_LEFT_BARREL_ROLL_DOWN_TO_UP || + track->type == TRACK_ELEM_RIGHT_BARREL_ROLL_DOWN_TO_UP + ) { + td6->flags |= (1 << 29); + } + + if (track->type == TRACK_ELEM_HALF_LOOP_UP || + track->type == TRACK_ELEM_HALF_LOOP_DOWN + ) { + td6->flags |= (1 << 18); + } + + if (track->type == TRACK_ELEM_LEFT_CORKSCREW_UP || + track->type == TRACK_ELEM_RIGHT_CORKSCREW_UP || + track->type == TRACK_ELEM_LEFT_CORKSCREW_DOWN || + track->type == TRACK_ELEM_RIGHT_CORKSCREW_DOWN + ) { + td6->flags |= (1 << 19); + } + + if (track->type == TRACK_ELEM_WATER_SPLASH) { + td6->flags |= (1 << 27); + } + + if (track->type == TRACK_ELEM_POWERED_LIFT) { + td6->flags |= (1 << 30); + } + + if (track->type == TRACK_ELEM_LEFT_LARGE_HALF_LOOP_UP || + track->type == TRACK_ELEM_RIGHT_LARGE_HALF_LOOP_UP || + track->type == TRACK_ELEM_RIGHT_LARGE_HALF_LOOP_DOWN || + track->type == TRACK_ELEM_LEFT_LARGE_HALF_LOOP_DOWN + ) { + td6->flags |= (1 << 31); + } + + if (track->type == TRACK_ELEM_LOG_FLUME_REVERSER) { + td6->flags2 |= TRACK_FLAGS2_CONTAINS_LOG_FLUME_REVERSER; + } + + uint8 bh; + if (track->type == TRACK_ELEM_BRAKES) { + bh = trackElement.element->properties.track.sequence >> 4; + } else { + bh = trackElement.element->properties.track.colour >> 4; + } + + uint8 flags = (trackElement.element->type & (1 << 7)) | bh; + flags |= (trackElement.element->properties.track.colour & 3) << 4; + if ( + RideData4[ride->type].flags & RIDE_TYPE_FLAG4_3 && + trackElement.element->properties.track.colour & (1 << 2) + ) { + flags |= (1 << 6); + } + + track->flags = flags; + track++; + + if (!track_block_get_next(&trackElement, &trackElement, NULL, NULL)) { + break; + } + + z = trackElement.element->base_height * 8; + direction = trackElement.element->type & MAP_ELEMENT_DIRECTION_MASK; + track_type = trackElement.element->properties.track.type; + + if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) { + break; + } + } while (trackElement.element != initialMap); + + trackElements = (uint8*)track; + // Mark the elements as finished. + *trackElements++ = 0xFF; + + rct_track_entrance *entrance = (rct_track_entrance*)trackElements; + + // First entrances, second exits + for (int i = 0; i < 2; i++) { + for (int station_index = 0; station_index < 4; station_index++) { + z = ride->station_heights[station_index]; + + uint16 location; + if (i == 0) { + location = ride->entrances[station_index]; + } else { + location = ride->exits[station_index]; + } + + if (location == 0xFFFF) { + continue; + } + + sint16 x = (location & 0xFF) * 32; + sint16 y = ((location & 0xFF00) >> 8) * 32; + + rct_map_element *map_element = map_get_first_element_at(x >> 5, y >> 5); + do { + if (map_element_get_type(map_element) != MAP_ELEMENT_TYPE_ENTRANCE) continue; + if (map_element->base_height == z) break; + } while (!map_element_is_last_for_tile(map_element++)); + // Add something that stops this from walking off the end + + uint8 entrance_direction = map_element->type & MAP_ELEMENT_DIRECTION_MASK; + entrance_direction -= RCT2_GLOBAL(0x00F4414D, uint8); + entrance_direction &= MAP_ELEMENT_DIRECTION_MASK; + entrance->direction = entrance_direction; + + x -= RCT2_GLOBAL(0x00F44142, sint16); + y -= RCT2_GLOBAL(0x00F44144, sint16); + + rotate_map_coordinates(&x, &y, RCT2_GLOBAL(0x00F4414D, uint8)); + entrance->x = x; + entrance->y = y; + + z *= 8; + z -= RCT2_GLOBAL(0x00F44146, sint16); + z /= 8; + + if (z > 127 || z < -126) { + gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + return 0; + } + + if (z == 0xFF) { + z = 0x80; + } + + entrance->z = z; + + // If this is the exit version + if (i == 1) { + entrance->direction |= (1 << 7); + } + entrance++; + } + } + + trackElements = (uint8*)entrance; + *trackElements++ = 0xFF; + *trackElements++ = 0xFF; + + RCT2_GLOBAL(0x00F44058, uint8*) = trackElements; + + sub_6D01B3(td6, PTD_OPERATION_DRAW_OUTLINES, 0, 4096, 4096, 0); + + // Resave global vars for scenery reasons. + RCT2_GLOBAL(0x00F44142, sint16) = start_x; + RCT2_GLOBAL(0x00F44144, sint16) = start_y; + RCT2_GLOBAL(0x00F44146, sint16) = start_z; + + RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, sint16) &= 0xFFF9; + RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, sint16) &= 0xFFF7; + + int x = RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MAX, sint16) - + RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_X_MIN, sint16); + + int y = RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_Y_MAX, sint16) - + RCT2_GLOBAL(RCT2_ADDRESS_TRACK_PREVIEW_Y_MIN, sint16); + + x /= 32; + y /= 32; + x++; + y++; + + td6->space_required_x = x; + td6->space_required_y = y; + return true; +} + +/** + * + * rct2: 0x006771DC but not really its branched from that + * quite far. + */ +static bool track_design_save_to_file(rct_track_td6 *track_design, utf8 *path) +{ + window_close_construction_windows(); + + // Encode TD6 data + uint8 *encodedData = malloc(0x8000); + int encodedDataLength = sawyercoding_encode_td6((uint8 *)track_design, encodedData, 0x609F); + + // Save encoded TD6 data to file + bool result; + log_verbose("saving track %s", path); + SDL_RWops *file = SDL_RWFromFile(path, "wb"); + if (file != NULL) { + SDL_RWwrite(file, encodedData, encodedDataLength, 1); + SDL_RWclose(file); + result = true; + } else { + log_error("Failed to save %s", path); + result = false; + } + + free(encodedData); + return result; +} diff --git a/src/windows/editor_object_selection.c b/src/windows/editor_object_selection.c index 4a2480d56a..f293daaa2e 100644 --- a/src/windows/editor_object_selection.c +++ b/src/windows/editor_object_selection.c @@ -1962,7 +1962,7 @@ static void window_editor_object_selection_manage_tracks() RCT2_GLOBAL(0xF44158, uint8) = ride_type; ride_list_item item = { ride_type, entry_index }; - track_load_list(item); + // track_load_list(item); window_track_list_open(item); } diff --git a/src/windows/install_track.c b/src/windows/install_track.c index 07af841ef1..7ee042f31e 100644 --- a/src/windows/install_track.c +++ b/src/windows/install_track.c @@ -118,7 +118,7 @@ void window_install_track_open(const char* path) RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_CACHE, void*) = mem; RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) = 0; _currentTrackPieceDirection = 2; - reset_track_list_cache(); + // reset_track_list_cache(); x = gScreenWidth / 2 - 201; y = max(28, gScreenHeight / 2 - 200); @@ -170,13 +170,13 @@ static void window_install_track_select(rct_window *w, int index) trackDesignItem = trackDesignList + (index * 128); RCT2_GLOBAL(0x00F4403C, utf8*) = trackDesignItem; - window_track_list_format_name( - (char*)0x009BC313, - trackDesignItem, - gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ? - 0 : - FORMAT_WHITE, - 1); + // window_track_list_format_name( + // (char*)0x009BC313, + // trackDesignItem, + // gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ? + // 0 : + // FORMAT_WHITE, + // 1); char track_path[MAX_PATH] = { 0 }; substitute_path(track_path, (char*)RCT2_ADDRESS_TRACKS_PATH, trackDesignItem); @@ -186,16 +186,16 @@ static void window_install_track_select(rct_window *w, int index) return; } - if (!load_track_design(track_path)) { - w->track_list.var_480 = 0xFFFF; - window_invalidate(w); - return; - } + // if (!load_track_design(track_path)) { + // w->track_list.var_480 = 0xFFFF; + // window_invalidate(w); + // return; + // } - trackDesign = track_get_info(index, NULL); - if (trackDesign == NULL) return; - if (trackDesign->track_td6.track_flags & 4) - window_error_open(STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE, -1); + // trackDesign = track_get_info(index, NULL); + // if (trackDesign == NULL) return; + // if (trackDesign->track_td6.track_flags & 4) + // window_error_open(STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE, -1); window_close(w); window_track_place_open(); @@ -230,7 +230,7 @@ static void window_install_track_mouseup(rct_window *w, int widgetIndex) break; case WIDX_TOGGLE_SCENERY: RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) ^= 1; - reset_track_list_cache(); + // reset_track_list_cache(); window_invalidate(w); break; case WIDX_INSTALL: @@ -244,7 +244,7 @@ static void window_install_track_mouseup(rct_window *w, int widgetIndex) } else{ // Copy the track name into the string buffer. - window_track_list_format_name(RCT2_ADDRESS(0x009BC677, char), track_dest_name, 0, 0); + // window_track_list_format_name(RCT2_ADDRESS(0x009BC677, char), track_dest_name, 0, 0); window_text_input_open(w, WIDX_INSTALL, 3383, 3384, 3165, 0, 255); } break; @@ -334,7 +334,7 @@ static void window_install_track_paint(rct_window *w, rct_drawpixelinfo *dpi) } // Track design name - window_track_list_format_name((char*)0x009BC677, (char*)0x009E3504, FORMAT_WINDOW_COLOUR_1, 1); + // window_track_list_format_name((char*)0x009BC677, (char*)0x009E3504, FORMAT_WINDOW_COLOUR_1, 1); gfx_draw_string_centred_clipped(dpi, 3165, NULL, 0, x, y, 368); // Information diff --git a/src/windows/new_ride.c b/src/windows/new_ride.c index adb07d92e3..8c1281ca95 100644 --- a/src/windows/new_ride.c +++ b/src/windows/new_ride.c @@ -842,7 +842,7 @@ static ride_list_item window_new_ride_scroll_get_ride_list_item_at(rct_window *w static int get_num_track_designs(ride_list_item item) { - track_load_list(item); + // track_load_list(item); char *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, char); int count = 0; @@ -943,7 +943,7 @@ static void window_new_ride_select(rct_window *w) #endif if (allowTrackDesigns && ride_type_has_flag(item.type, RIDE_TYPE_FLAG_HAS_TRACK)) { - track_load_list(item); + // track_load_list(item); char *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, char); if (*trackDesignList != 0) { diff --git a/src/windows/ride.c b/src/windows/ride.c index 6e6fb451b7..bf25932c46 100644 --- a/src/windows/ride.c +++ b/src/windows/ride.c @@ -4663,7 +4663,7 @@ static void setup_scenery_selection(rct_window* w){ */ static void window_ride_measurements_design_reset() { - track_save_reset_scenery(); + track_design_save_reset_scenery(); } /** @@ -4672,7 +4672,7 @@ static void window_ride_measurements_design_reset() */ static void window_ride_measurements_design_select_nearby_scenery() { - track_save_select_nearby_scenery(RCT2_GLOBAL(0x00F64DE8, uint8)); + track_design_save_select_nearby_scenery(RCT2_GLOBAL(0x00F64DE8, uint8)); } /** @@ -4691,7 +4691,7 @@ static void window_ride_measurements_design_cancel() */ static void window_ride_measurements_design_save(rct_window *w) { - if (save_track_design((uint8)w->number) == 0) return; + if (track_design_save((uint8)w->number) == 0) return; window_ride_measurements_design_cancel(); } @@ -4791,7 +4791,7 @@ static void window_ride_measurements_dropdown(rct_window *w, int widgetIndex, in dropdownIndex = gDropdownHighlightedIndex; if (dropdownIndex == 0) - save_track_design((uint8)w->number); + track_design_save((uint8)w->number); else setup_scenery_selection(w); } @@ -4823,7 +4823,7 @@ static void window_ride_measurements_tooldown(rct_window *w, int widgetIndex, in case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: case VIEWPORT_INTERACTION_ITEM_WALL: case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - track_save_toggle_map_element(interactionType, mapX, mapY, mapElement); + track_design_save_toggle_map_element(interactionType, mapX, mapY, mapElement); break; } } diff --git a/src/windows/track_list.c b/src/windows/track_list.c index ffaf17856f..c3c19484ba 100644 --- a/src/windows/track_list.c +++ b/src/windows/track_list.c @@ -123,7 +123,7 @@ void window_track_list_open(ride_list_item item) return; RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_CACHE, void*) = mem; - reset_track_list_cache(); + // reset_track_list_cache(); if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { x = gScreenWidth / 2 - 300; @@ -191,21 +191,21 @@ static void window_track_list_select(rct_window *w, int index) return; } - if (!load_track_design(tdRef->path)) { - w->track_list.var_480 = 0xFFFF; - window_invalidate(w); - return; - } + // if (!load_track_design(tdRef->path)) { + // w->track_list.var_480 = 0xFFFF; + // window_invalidate(w); + // return; + // } - rct_track_design *trackDesign = track_get_info(index, NULL); - if (trackDesign != NULL) { - if (trackDesign->track_td6.track_flags & 4) { - window_error_open(STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE, STR_NONE); - } - - window_close(w); - window_track_place_open(); - } + // rct_track_design *trackDesign = track_get_info(index, NULL); + // if (trackDesign != NULL) { + // if (trackDesign->track_td6.track_flags & 4) { + // window_error_open(STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE, STR_NONE); + // } + // + // window_close(w); + // window_track_place_open(); + // } } static int window_track_list_get_list_item_index_from_position(int x, int y) @@ -261,7 +261,7 @@ static void window_track_list_mouseup(rct_window *w, int widgetIndex) break; case WIDX_TOGGLE_SCENERY: RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) ^= 1; - reset_track_list_cache(); + // reset_track_list_cache(); window_invalidate(w); break; case WIDX_BACK: diff --git a/src/windows/track_manage.c b/src/windows/track_manage.c index 6d83a9a45e..4599362df4 100644 --- a/src/windows/track_manage.c +++ b/src/windows/track_manage.c @@ -217,12 +217,12 @@ static void window_track_manage_textinput(rct_window *w, int widgetIndex, char * if (widgetIndex != WIDX_RENAME || text == NULL) return; - if (track_rename(text)) { - window_close_by_class(WC_TRACK_DELETE_PROMPT); - window_close(w); - } else { - window_error_open(STR_CANT_RENAME_TRACK_DESIGN, gGameCommandErrorText); - } + // if (track_rename(text)) { + // window_close_by_class(WC_TRACK_DELETE_PROMPT); + // window_close(w); + // } else { + // window_error_open(STR_CANT_RENAME_TRACK_DESIGN, gGameCommandErrorText); + // } } static void window_track_manage_invalidate(rct_window *w) diff --git a/src/world/scenery.h b/src/world/scenery.h index a793b0f24b..ad70ce05aa 100644 --- a/src/world/scenery.h +++ b/src/world/scenery.h @@ -19,6 +19,7 @@ #include "../common.h" #include "../object.h" +#include "../world/map.h" typedef struct { uint32 flags; // 0x06