From 54d6722bf22bdefe6d3fe6baef49e5a5bf2881e8 Mon Sep 17 00:00:00 2001 From: duncanspumpkin Date: Sun, 30 Jun 2019 11:23:06 +0100 Subject: [PATCH] Link everything together --- src/openrct2-ui/WindowManager.cpp | 4 +- src/openrct2-ui/windows/LoadSave.cpp | 12 +- src/openrct2-ui/windows/Ride.cpp | 44 +- src/openrct2-ui/windows/Window.h | 4 +- src/openrct2/rct2/T6Exporter.cpp | 599 ++------------------------- src/openrct2/rct2/T6Exporter.h | 12 +- src/openrct2/ride/Ride.cpp | 26 ++ src/openrct2/ride/Ride.h | 4 + src/openrct2/ride/TrackDesign.cpp | 475 ++++++++++++++++++++- src/openrct2/ride/TrackDesign.h | 10 + 10 files changed, 610 insertions(+), 580 deletions(-) diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index 38ab63a8ed..1c10ff5d27 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -222,7 +223,8 @@ public: uint32_t type = intent->GetUIntExtra(INTENT_EXTRA_LOADSAVE_TYPE); std::string defaultName = intent->GetStringExtra(INTENT_EXTRA_PATH); loadsave_callback callback = (loadsave_callback)intent->GetPointerExtra(INTENT_EXTRA_CALLBACK); - rct_window* w = window_loadsave_open(type, defaultName.c_str(), callback); + TrackDesign* trackDesign = static_cast(intent->GetPointerExtra(INTENT_EXTRA_TRACK_DESIGN)); + rct_window* w = window_loadsave_open(type, defaultName.c_str(), callback, trackDesign); return w; } diff --git a/src/openrct2-ui/windows/LoadSave.cpp b/src/openrct2-ui/windows/LoadSave.cpp index eb1e984f8e..8cb6f195ad 100644 --- a/src/openrct2-ui/windows/LoadSave.cpp +++ b/src/openrct2-ui/windows/LoadSave.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,7 @@ struct LoadSaveListItem }; static loadsave_callback _loadSaveCallback; +static TrackDesign* _trackDesign; static std::vector _listItems; static char _directory[MAX_PATH]; @@ -250,9 +252,10 @@ static int32_t window_loadsave_get_dir(const int32_t type, char* path, size_t pa static bool browse(bool isSave, char* path, size_t pathSize); -rct_window* window_loadsave_open(int32_t type, const char* defaultName, loadsave_callback callback) +rct_window* window_loadsave_open(int32_t type, const char* defaultName, loadsave_callback callback, TrackDesign* trackDesign) { _loadSaveCallback = callback; + _trackDesign = trackDesign; _type = type; _defaultName[0] = '\0'; @@ -1081,8 +1084,13 @@ static void window_loadsave_select(rct_window* w, const char* path) case (LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK): { + save_path(&gConfigGeneral.last_save_track_directory, pathBuffer); + path_set_extension(pathBuffer, "td6", sizeof(pathBuffer)); - int32_t success = 0; // track_design_save_to_file(pathBuffer); + + T6Exporter t6Export{ _trackDesign }; + + auto success = t6Export.SaveTrack(pathBuffer); if (success) { diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 2bb842052a..0a29e6a199 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -950,6 +951,7 @@ static rct_window_event_list *window_ride_page_events[] = { static bool _collectTrackDesignScenery = false; static int32_t _lastSceneryX = 0; static int32_t _lastSceneryY = 0; +static std::unique_ptr _trackDesign; // Cached overall view for each ride // (Re)calculated when the ride window is opened @@ -5403,16 +5405,48 @@ void window_ride_measurements_design_cancel() } } +void TrackDesignCallback(int32_t result, [[maybe_unused]] const utf8* path) +{ + if (result == MODAL_RESULT_OK) + { + track_repository_scan(); + } + gfx_invalidate_screen(); +}; + /** * * rct2: 0x006AD4CD */ static void window_ride_measurements_design_save(rct_window* w) { - T6Exporter t6Exporter; - t6Exporter.Save((ride_id_t)w->number); - t6Exporter.SaveTrack("test.td6"); - // track_design_save((uint8_t)w->number); + Ride* ride = get_ride(w->number); + _trackDesign = ride->SaveToTrackDesign(); + if (!_trackDesign) + { + return; + } + + if (gTrackDesignSaveMode) + { + auto errMessage = _trackDesign->CreateTrackDesignScenery(); + if (errMessage != STR_NONE) + { + context_show_error(STR_CANT_SAVE_TRACK_DESIGN, errMessage); + return; + } + } + + utf8 track_name[256]; + format_string(track_name, sizeof(track_name), ride->name, &ride->name_arguments); + + auto intent = Intent(WC_LOADSAVE); + intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK); + intent.putExtra(INTENT_EXTRA_TRACK_DESIGN, _trackDesign.get()); + intent.putExtra(INTENT_EXTRA_PATH, std::string{ track_name }); + intent.putExtra(INTENT_EXTRA_CALLBACK, static_cast(&TrackDesignCallback)); + + context_open_intent(&intent); } /** @@ -5516,8 +5550,8 @@ static void window_ride_measurements_dropdown(rct_window* w, rct_widgetindex wid if (dropdownIndex == 0) { + window_ride_measurements_design_save(w); } - // track_design_save((uint8_t)w->number); else setup_scenery_selection(w); } diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h index 76c45cb3f4..26f543f269 100644 --- a/src/openrct2-ui/windows/Window.h +++ b/src/openrct2-ui/windows/Window.h @@ -90,8 +90,8 @@ void window_title_command_editor_open(struct TitleSequence* sequence, int32_t co rct_window* window_scenarioselect_open(scenarioselect_callback callback, bool titleEditor); rct_window* window_error_open(rct_string_id title, rct_string_id message); - -rct_window* window_loadsave_open(int32_t type, const char* defaultName, loadsave_callback callback); +struct TrackDesign; +rct_window* window_loadsave_open(int32_t type, const char* defaultName, loadsave_callback callback, TrackDesign* t6Exporter); rct_window* window_track_place_open(const struct track_design_file_ref* tdFileRef); rct_window* window_track_manage_open(struct track_design_file_ref* tdFileRef); diff --git a/src/openrct2/rct2/T6Exporter.cpp b/src/openrct2/rct2/T6Exporter.cpp index e656cc6c58..bf830861a4 100644 --- a/src/openrct2/rct2/T6Exporter.cpp +++ b/src/openrct2/rct2/T6Exporter.cpp @@ -25,7 +25,7 @@ #include "../windows/Intent.h" #include -T6Exporter::T6Exporter() +T6Exporter::T6Exporter(TrackDesign* trackDesign) : _trackDesign(trackDesign) { } @@ -38,47 +38,47 @@ bool T6Exporter::SaveTrack(const utf8* path) bool T6Exporter::SaveTrack(IStream* stream) { MemoryStream tempStream; - tempStream.WriteValue(_trackDesign.type); - tempStream.WriteValue(_trackDesign.vehicle_type); - tempStream.WriteValue(_trackDesign.flags); - tempStream.WriteValue(_trackDesign.ride_mode); - tempStream.WriteValue((_trackDesign.colour_scheme & 0x3) | (2 << 2)); - tempStream.WriteArray(_trackDesign.vehicle_colours, RCT12_MAX_VEHICLE_COLOURS); + tempStream.WriteValue(_trackDesign->type); + tempStream.WriteValue(_trackDesign->vehicle_type); + tempStream.WriteValue(_trackDesign->flags); + tempStream.WriteValue(_trackDesign->ride_mode); + tempStream.WriteValue((_trackDesign->colour_scheme & 0x3) | (2 << 2)); + tempStream.WriteArray(_trackDesign->vehicle_colours, RCT12_MAX_VEHICLE_COLOURS); tempStream.WriteValue(0); - tempStream.WriteValue(_trackDesign.entrance_style); - tempStream.WriteValue(_trackDesign.total_air_time); - tempStream.WriteValue(_trackDesign.depart_flags); - tempStream.WriteValue(_trackDesign.number_of_trains); - tempStream.WriteValue(_trackDesign.number_of_cars_per_train); - tempStream.WriteValue(_trackDesign.min_waiting_time); - tempStream.WriteValue(_trackDesign.max_waiting_time); - tempStream.WriteValue(_trackDesign.operation_setting); - tempStream.WriteValue(_trackDesign.max_speed); - tempStream.WriteValue(_trackDesign.average_speed); - tempStream.WriteValue(_trackDesign.ride_length); - tempStream.WriteValue(_trackDesign.max_positive_vertical_g); - tempStream.WriteValue(_trackDesign.max_negative_vertical_g); - tempStream.WriteValue(_trackDesign.max_lateral_g); - tempStream.WriteValue(_trackDesign.type == RIDE_TYPE_MINI_GOLF ? _trackDesign.holes : _trackDesign.inversions); - tempStream.WriteValue(_trackDesign.drops); - tempStream.WriteValue(_trackDesign.highest_drop_height); - tempStream.WriteValue(_trackDesign.excitement); - tempStream.WriteValue(_trackDesign.intensity); - tempStream.WriteValue(_trackDesign.nausea); - tempStream.WriteValue(_trackDesign.upkeep_cost); - tempStream.WriteArray(_trackDesign.track_spine_colour, RCT12_NUM_COLOUR_SCHEMES); - tempStream.WriteArray(_trackDesign.track_rail_colour, RCT12_NUM_COLOUR_SCHEMES); - tempStream.WriteArray(_trackDesign.track_support_colour, RCT12_NUM_COLOUR_SCHEMES); - tempStream.WriteValue(_trackDesign.flags2); - tempStream.Write(&_trackDesign.vehicle_object, sizeof(rct_object_entry)); - tempStream.WriteValue(_trackDesign.space_required_x); - tempStream.WriteValue(_trackDesign.space_required_y); - tempStream.WriteArray(_trackDesign.vehicle_additional_colour, RCT2_MAX_CARS_PER_TRAIN); - tempStream.WriteValue(_trackDesign.lift_hill_speed | (_trackDesign.num_circuits << 5)); + tempStream.WriteValue(_trackDesign->entrance_style); + tempStream.WriteValue(_trackDesign->total_air_time); + tempStream.WriteValue(_trackDesign->depart_flags); + tempStream.WriteValue(_trackDesign->number_of_trains); + tempStream.WriteValue(_trackDesign->number_of_cars_per_train); + tempStream.WriteValue(_trackDesign->min_waiting_time); + tempStream.WriteValue(_trackDesign->max_waiting_time); + tempStream.WriteValue(_trackDesign->operation_setting); + tempStream.WriteValue(_trackDesign->max_speed); + tempStream.WriteValue(_trackDesign->average_speed); + tempStream.WriteValue(_trackDesign->ride_length); + tempStream.WriteValue(_trackDesign->max_positive_vertical_g); + tempStream.WriteValue(_trackDesign->max_negative_vertical_g); + tempStream.WriteValue(_trackDesign->max_lateral_g); + tempStream.WriteValue(_trackDesign->type == RIDE_TYPE_MINI_GOLF ? _trackDesign->holes : _trackDesign->inversions); + tempStream.WriteValue(_trackDesign->drops); + tempStream.WriteValue(_trackDesign->highest_drop_height); + tempStream.WriteValue(_trackDesign->excitement); + tempStream.WriteValue(_trackDesign->intensity); + tempStream.WriteValue(_trackDesign->nausea); + tempStream.WriteValue(_trackDesign->upkeep_cost); + tempStream.WriteArray(_trackDesign->track_spine_colour, RCT12_NUM_COLOUR_SCHEMES); + tempStream.WriteArray(_trackDesign->track_rail_colour, RCT12_NUM_COLOUR_SCHEMES); + tempStream.WriteArray(_trackDesign->track_support_colour, RCT12_NUM_COLOUR_SCHEMES); + tempStream.WriteValue(_trackDesign->flags2); + tempStream.Write(&_trackDesign->vehicle_object, sizeof(rct_object_entry)); + tempStream.WriteValue(_trackDesign->space_required_x); + tempStream.WriteValue(_trackDesign->space_required_y); + tempStream.WriteArray(_trackDesign->vehicle_additional_colour, RCT2_MAX_CARS_PER_TRAIN); + tempStream.WriteValue(_trackDesign->lift_hill_speed | (_trackDesign->num_circuits << 5)); - if (_trackDesign.type == RIDE_TYPE_MAZE) + if (_trackDesign->type == RIDE_TYPE_MAZE) { - for (const auto &mazeElement : _trackDesign.maze_elements) + for (const auto &mazeElement : _trackDesign->maze_elements) { tempStream.WriteValue(mazeElement.all); } @@ -87,7 +87,7 @@ bool T6Exporter::SaveTrack(IStream* stream) } else { - for (const auto &trackElement : _trackDesign.track_elements) + for (const auto &trackElement : _trackDesign->track_elements) { tempStream.WriteValue(trackElement.type); tempStream.WriteValue(trackElement.flags); @@ -95,7 +95,7 @@ bool T6Exporter::SaveTrack(IStream* stream) tempStream.WriteValue(0xFF); - for (const auto &entranceElement : _trackDesign.entrance_elements) + for (const auto &entranceElement : _trackDesign->entrance_elements) { tempStream.WriteValue(entranceElement.z); tempStream.WriteValue(entranceElement.direction); @@ -106,7 +106,7 @@ bool T6Exporter::SaveTrack(IStream* stream) tempStream.WriteValue(0xFF); } - for (const auto &sceneryElement : _trackDesign.scenery_elements) + for (const auto &sceneryElement : _trackDesign->scenery_elements) { tempStream.Write(&sceneryElement.scenery_object, sizeof(rct_object_entry)); tempStream.WriteValue(sceneryElement.x); @@ -123,518 +123,3 @@ bool T6Exporter::SaveTrack(IStream* stream) sawyerCoding.WriteChunkTrack(tempStream.GetData(), tempStream.GetLength()); return true; } - -bool T6Exporter::Save(ride_id_t rideId) -{ - Ride* ride = get_ride(rideId); - - if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE); - return false; - } - - if (!ride_has_ratings(ride)) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE); - return false; - } - - auto errMessage = CreateTrackDesign(*ride); - if (errMessage != STR_NONE) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, errMessage); - return false; - } - - if (gTrackDesignSaveMode) - { - if (!CreateTrackDesignScenery(*ride)) - { - return false; - } - } - - utf8 track_name[256]; - format_string(track_name, sizeof(track_name), ride->name, &ride->name_arguments); - - auto intent = Intent(WC_LOADSAVE); - intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK); - intent.putExtra(INTENT_EXTRA_PATH, std::string{ track_name }); - intent.putExtra(INTENT_EXTRA_CALLBACK, []() { - track_repository_scan(); - gfx_invalidate_screen(); - }); - - context_open_intent(&intent); - - return true; -} - -rct_string_id T6Exporter::CreateTrackDesign(Ride& ride) -{ - _trackDesign.type = ride.type; - auto object = object_entry_get_entry(OBJECT_TYPE_RIDE, ride.subtype); - - // Note we are only copying rct_object_entry in size and - // not the extended as we don't need the chunk size. - std::memcpy(&_trackDesign.vehicle_object, object, sizeof(rct_object_entry)); - - _trackDesign.ride_mode = ride.mode; - _trackDesign.colour_scheme = ride.colour_scheme_type & 3; - - for (int32_t i = 0; i < RCT12_MAX_VEHICLES_PER_RIDE; i++) - { - _trackDesign.vehicle_colours[i].body_colour = ride.vehicle_colours[i].Body; - _trackDesign.vehicle_colours[i].trim_colour = ride.vehicle_colours[i].Trim; - _trackDesign.vehicle_additional_colour[i] = ride.vehicle_colours[i].Ternary; - } - - for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) - { - _trackDesign.track_spine_colour[i] = ride.track_colour[i].main; - _trackDesign.track_rail_colour[i] = ride.track_colour[i].additional; - _trackDesign.track_support_colour[i] = ride.track_colour[i].supports; - } - - _trackDesign.depart_flags = ride.depart_flags; - _trackDesign.number_of_trains = ride.num_vehicles; - _trackDesign.number_of_cars_per_train = ride.num_cars_per_train; - _trackDesign.min_waiting_time = ride.min_waiting_time; - _trackDesign.max_waiting_time = ride.max_waiting_time; - _trackDesign.operation_setting = ride.operation_option; - _trackDesign.lift_hill_speed = ride.lift_hill_speed; - _trackDesign.num_circuits = ride.num_circuits; - - _trackDesign.entrance_style = ride.entrance_style; - _trackDesign.max_speed = (int8_t)(ride.max_speed / 65536); - _trackDesign.average_speed = (int8_t)(ride.average_speed / 65536); - _trackDesign.ride_length = ride_get_total_length(&ride) / 65536; - _trackDesign.max_positive_vertical_g = ride.max_positive_vertical_g / 32; - _trackDesign.max_negative_vertical_g = ride.max_negative_vertical_g / 32; - _trackDesign.max_lateral_g = ride.max_lateral_g / 32; - _trackDesign.inversions = ride.holes & 0x1F; - _trackDesign.inversions = ride.inversions & 0x1F; - _trackDesign.inversions |= (ride.sheltered_eighths << 5); - _trackDesign.drops = ride.drops; - _trackDesign.highest_drop_height = ride.highest_drop_height; - - uint16_t total_air_time = (ride.total_air_time * 123) / 1024; - if (total_air_time > 255) - { - total_air_time = 0; - } - _trackDesign.total_air_time = (uint8_t)total_air_time; - - _trackDesign.excitement = ride.ratings.excitement / 10; - _trackDesign.intensity = ride.ratings.intensity / 10; - _trackDesign.nausea = ride.ratings.nausea / 10; - - _trackDesign.upkeep_cost = ride.upkeep_cost; - _trackDesign.flags = 0; - _trackDesign.flags2 = 0; - - if (_trackDesign.type == RIDE_TYPE_MAZE) - { - return CreateTrackDesignMaze(ride); - } - else - { - return CreateTrackDesignTrack(ride); - } -} - -rct_string_id T6Exporter::CreateTrackDesignTrack(Ride& ride) -{ - CoordsXYE trackElement; - if (!ride_try_get_origin_element(&ride, &trackElement)) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - - ride_get_start_of_track(&trackElement); - - int32_t z = trackElement.element->base_height * 8; - uint8_t track_type = trackElement.element->AsTrack()->GetTrackType(); - uint8_t direction = trackElement.element->GetDirection(); - _saveDirection = direction; - - if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - - const rct_track_coordinates* trackCoordinates = &TrackCoordinates[trackElement.element->AsTrack()->GetTrackType()]; - // Used in the following loop to know when we have - // completed all of the elements and are back at the - // start. - TileElement* initialMap = trackElement.element; - - int16_t start_x = trackElement.x; - int16_t start_y = trackElement.y; - int16_t start_z = z + trackCoordinates->z_begin; - gTrackPreviewOrigin = { start_x, start_y, start_z }; - - do - { - rct_td6_track_element track{}; - track.type = trackElement.element->AsTrack()->GetTrackType(); - // TODO move to RCT2 limit - if (track.type == TRACK_ELEM_255) - { - track.type = TRACK_ELEM_255_ALIAS; - } - - uint8_t flags; - if (track_element_has_speed_setting(track.type)) - { - flags = trackElement.element->AsTrack()->GetBrakeBoosterSpeed() >> 1; - } - else - { - flags = trackElement.element->AsTrack()->GetSeatRotation(); - } - - if (trackElement.element->AsTrack()->HasChain()) - flags |= (1 << 7); - flags |= trackElement.element->AsTrack()->GetColourScheme() << 4; - if (RideData4[ride.type].flags & RIDE_TYPE_FLAG4_HAS_ALTERNATIVE_TRACK_TYPE - && trackElement.element->AsTrack()->IsInverted()) - { - flags |= TRACK_ELEMENT_FLAG_INVERTED; - } - - track.flags = flags; - _trackDesign.track_elements.push_back(track); - - if (!track_block_get_next(&trackElement, &trackElement, nullptr, nullptr)) - { - break; - } - - z = trackElement.element->base_height * 8; - direction = trackElement.element->GetDirection(); - track_type = trackElement.element->AsTrack()->GetTrackType(); - - if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) - { - break; - } - - // TODO move to RCT2 limit - constexpr auto TD6MaxTrackElements = 8192; - - if (_trackDesign.track_elements.size() > TD6MaxTrackElements) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - } while (trackElement.element != initialMap); - - // First entrances, second exits - for (int32_t i = 0; i < 2; i++) - { - for (int32_t station_index = 0; station_index < RCT12_MAX_STATIONS_PER_RIDE; station_index++) - { - z = ride.stations[station_index].Height; - - TileCoordsXYZD location; - if (i == 0) - { - location = ride_get_entrance_location(&ride, station_index); - } - else - { - location = ride_get_exit_location(&ride, station_index); - } - - if (location.isNull()) - { - continue; - } - - int16_t x = location.x * 32; - int16_t y = location.y * 32; - - TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); - do - { - if (tileElement == nullptr) - break; - if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - continue; - if (tileElement->base_height == z) - break; - } while (!(tileElement++)->IsLastForTile()); - - if (tileElement == nullptr) - { - continue; - } - // Add something that stops this from walking off the end - - Direction entranceDirection = tileElement->GetDirection(); - entranceDirection -= _saveDirection; - entranceDirection &= TILE_ELEMENT_DIRECTION_MASK; - - rct_td6_entrance_element entrance{}; - entrance.direction = entranceDirection; - - x -= gTrackPreviewOrigin.x; - y -= gTrackPreviewOrigin.y; - - // Rotate entrance coordinates backwards to the correct direction - rotate_map_coordinates(&x, &y, (0 - _saveDirection) & 3); - entrance.x = x; - entrance.y = y; - - z *= 8; - z -= gTrackPreviewOrigin.z; - z /= 8; - - if (z > 127 || z < -126) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - - if (z == 0xFF) - { - z = 0x80; - } - - entrance.z = z; - - // If this is the exit version - if (i == 1) - { - entrance.direction |= (1 << 7); - } - _trackDesign.entrance_elements.push_back(entrance); - } - } - - place_virtual_track(&_trackDesign, PTD_OPERATION_DRAW_OUTLINES, true, get_ride(0), 4096, 4096, 0); - - // Resave global vars for scenery reasons. - gTrackPreviewOrigin = { start_x, start_y, start_z }; - - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; - - _trackDesign.space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; - _trackDesign.space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; - return 0; -} - -rct_string_id T6Exporter::CreateTrackDesignMaze(Ride& ride) -{ - auto startLoc = MazeGetFirstElement(ride); - - if (startLoc.element == nullptr) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - - gTrackPreviewOrigin = { static_cast(startLoc.x), static_cast(startLoc.y), - (int16_t)(startLoc.element->base_height * 8) }; - - // 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 (int16_t y = startLoc.y, x = startLoc.y; y < 8192; y += 32) - { - for (; x < 8192; x += 32) - { - auto tileElement = map_get_first_element_at(x / 32, y / 32); - do - { - if (tileElement == nullptr) - break; - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (tileElement->AsTrack()->GetRideIndex() != ride.id) - continue; - - rct_td6_maze_element maze{}; - - maze.maze_entry = tileElement->AsTrack()->GetMazeEntry(); - maze.x = (x - startLoc.x) / 32; - maze.y = (y - startLoc.y) / 32; - - _trackDesign.maze_elements.push_back(maze); - - if (_trackDesign.maze_elements.size() >= 2000) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - } while (!(tileElement++)->IsLastForTile()); - } - x = 0; - } - - auto location = ride_get_entrance_location(&ride, 0); - if (location.isNull()) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - - CoordsXY entranceLoc = { location.x * 32, location.y * 32 }; - auto tileElement = map_get_first_element_at(location.x, location.y); - do - { - if (tileElement == nullptr) - break; - if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetRideIndex() == ride.id) - break; - } while (!(tileElement++)->IsLastForTile()); - // Add something that stops this from walking off the end - - uint8_t entranceDirection = tileElement->GetDirection(); - rct_td6_maze_element mazeEntrance{}; - mazeEntrance.direction = entranceDirection; - mazeEntrance.type = 8; - mazeEntrance.x = (int8_t)((entranceLoc.x - startLoc.x) / 32); - mazeEntrance.y = (int8_t)((entranceLoc.y - startLoc.y) / 32); - _trackDesign.maze_elements.push_back(mazeEntrance); - - location = ride_get_exit_location(&ride, 0); - if (location.isNull()) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - - CoordsXY exitLoc = { location.x * 32, location.y * 32 }; - tileElement = map_get_first_element_at(location.x, location.y); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT) - continue; - if (tileElement->AsEntrance()->GetRideIndex() == ride.id) - break; - } while (!(tileElement++)->IsLastForTile()); - // Add something that stops this from walking off the end - - uint8_t exit_direction = tileElement->GetDirection(); - rct_td6_maze_element mazeExit{}; - mazeExit.direction = exit_direction; - mazeExit.type = 0x80; - mazeExit.x = (int8_t)((exitLoc.x - startLoc.x) / 32); - mazeExit.y = (int8_t)((exitLoc.y - startLoc.y) / 32); - _trackDesign.maze_elements.push_back(mazeExit); - - // Save global vars as they are still used by scenery???? - int16_t startZ = gTrackPreviewOrigin.z; - place_virtual_track(&_trackDesign, PTD_OPERATION_DRAW_OUTLINES, true, get_ride(0), 4096, 4096, 0); - gTrackPreviewOrigin = { static_cast(startLoc.x), static_cast(startLoc.y), startZ }; - - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; - - _trackDesign.space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; - _trackDesign.space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; - return 0; -} - -CoordsXYE T6Exporter::MazeGetFirstElement(Ride& ride) -{ - CoordsXYE tile{}; - for (tile.y = 0; tile.y < 8192; tile.y += 32) - { - for (tile.x = 0; tile.x < 8192; tile.x += 32) - { - tile.element = map_get_first_element_at(tile.x / 32, tile.y / 32); - do - { - if (tile.element == nullptr) - break; - - if (tile.element->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (tile.element->AsTrack()->GetRideIndex() == ride.id) - { - return tile; - } - } while (!(tile.element++)->IsLastForTile()); - } - } - tile.element = nullptr; - return tile; -} - -rct_string_id T6Exporter::CreateTrackDesignScenery([[maybe_unused]] Ride& ride) -{ - _trackDesign.scenery_elements = _trackSavedTileElementsDesc; - // Run an element loop - for (auto& scenery : _trackDesign.scenery_elements) - { - switch (object_entry_get_type(&scenery.scenery_object)) - { - case OBJECT_TYPE_PATHS: { - uint8_t slope = (scenery.flags & 0x60) >> 5; - slope -= _saveDirection; - - scenery.flags &= 0x9F; - scenery.flags |= ((slope & 3) << 5); - - // Direction of connection on path - uint8_t direction = scenery.flags & 0xF; - // Rotate the direction by the track direction - direction = ((direction << 4) >> _saveDirection); - - scenery.flags &= 0xF0; - scenery.flags |= (direction & 0xF) | (direction >> 4); - break; - } - case OBJECT_TYPE_WALLS: { - uint8_t direction = scenery.flags & 3; - direction -= _saveDirection; - - scenery.flags &= 0xFC; - scenery.flags |= (direction & 3); - break; - } - default: { - uint8_t direction = scenery.flags & 3; - uint8_t quadrant = (scenery.flags & 0x0C) >> 2; - - direction -= _saveDirection; - quadrant -= _saveDirection; - - scenery.flags &= 0xF0; - scenery.flags |= (direction & 3) | ((quadrant & 3) << 2); - break; - } - } - - int16_t x = ((uint8_t)scenery.x) * 32 - gTrackPreviewOrigin.x; - int16_t y = ((uint8_t)scenery.y) * 32 - gTrackPreviewOrigin.y; - rotate_map_coordinates(&x, &y, (0 - _saveDirection) & 3); - x /= 32; - y /= 32; - - if (x > 127 || y > 127 || x < -126 || y < -126) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - - scenery.x = (int8_t)x; - scenery.y = (int8_t)y; - - int32_t z = scenery.z * 8 - gTrackPreviewOrigin.z; - z /= 8; - if (z > 127 || z < -126) - { - return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - } - scenery.z = z; - } - - return true; -} diff --git a/src/openrct2/rct2/T6Exporter.h b/src/openrct2/rct2/T6Exporter.h index bb542954e6..5302a8ef3e 100644 --- a/src/openrct2/rct2/T6Exporter.h +++ b/src/openrct2/rct2/T6Exporter.h @@ -15,7 +15,6 @@ #include interface IStream; -struct Ride; /** * Class to export RollerCoaster Tycoon 2 track designs (*.TD6). @@ -23,18 +22,11 @@ struct Ride; class T6Exporter final { public: - T6Exporter(); + T6Exporter(TrackDesign* trackDesign); - bool Save(ride_id_t rideId); bool SaveTrack(const utf8* path); bool SaveTrack(IStream* stream); private: - TrackDesign _trackDesign; - Direction _saveDirection; - rct_string_id CreateTrackDesign(Ride& ride); - rct_string_id CreateTrackDesignTrack(Ride& ride); - rct_string_id CreateTrackDesignMaze(Ride& ride); - rct_string_id CreateTrackDesignScenery(Ride& ride); - CoordsXYE MazeGetFirstElement(Ride& ride); + TrackDesign* _trackDesign; }; diff --git a/src/openrct2/ride/Ride.cpp b/src/openrct2/ride/Ride.cpp index b8f8872b85..f466557002 100644 --- a/src/openrct2/ride/Ride.cpp +++ b/src/openrct2/ride/Ride.cpp @@ -60,6 +60,7 @@ #include "Station.h" #include "Track.h" #include "TrackData.h" +#include "TrackDesign.h" #include #include @@ -2010,6 +2011,31 @@ void Ride::UpdateAll() ride_music_update_final(); } +std::unique_ptr Ride::SaveToTrackDesign() const +{ + if (!(lifecycle_flags & RIDE_LIFECYCLE_TESTED)) + { + context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE); + return nullptr; + } + + if (!ride_has_ratings(this)) + { + context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE); + return nullptr; + } + + auto td = std::make_unique(); + auto errMessage = td->CreateTrackDesign(*this); + if (errMessage != STR_NONE) + { + context_show_error(STR_CANT_SAVE_TRACK_DESIGN, errMessage); + return nullptr; + } + + return td; +} + /** * * rct2: 0x006ABE73 diff --git a/src/openrct2/ride/Ride.h b/src/openrct2/ride/Ride.h index d22d9650fb..53037cd572 100644 --- a/src/openrct2/ride/Ride.h +++ b/src/openrct2/ride/Ride.h @@ -202,6 +202,8 @@ enum class RideClassification KioskOrFacility }; +struct TrackDesign; + /** * Ride structure. * @@ -441,6 +443,8 @@ public: static void UpdateAll(); static bool NameExists(const std::string_view& name, ride_id_t excludeRideId = RIDE_ID_NULL); + + std::unique_ptr SaveToTrackDesign() const; }; #pragma pack(push, 1) diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 592140a462..f45b6e459a 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -95,15 +95,484 @@ static void track_design_preview_restore_map(map_backup* backup); static void track_design_preview_clear_map(); +rct_string_id TrackDesign::CreateTrackDesign(const Ride& ride) +{ + type = ride.type; + auto object = object_entry_get_entry(OBJECT_TYPE_RIDE, ride.subtype); + + // Note we are only copying rct_object_entry in size and + // not the extended as we don't need the chunk size. + std::memcpy(&vehicle_object, object, sizeof(rct_object_entry)); + + ride_mode = ride.mode; + colour_scheme = ride.colour_scheme_type & 3; + + for (int32_t i = 0; i < RCT12_MAX_VEHICLES_PER_RIDE; i++) + { + vehicle_colours[i].body_colour = ride.vehicle_colours[i].Body; + vehicle_colours[i].trim_colour = ride.vehicle_colours[i].Trim; + vehicle_additional_colour[i] = ride.vehicle_colours[i].Ternary; + } + + for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) + { + track_spine_colour[i] = ride.track_colour[i].main; + track_rail_colour[i] = ride.track_colour[i].additional; + track_support_colour[i] = ride.track_colour[i].supports; + } + + depart_flags = ride.depart_flags; + number_of_trains = ride.num_vehicles; + number_of_cars_per_train = ride.num_cars_per_train; + min_waiting_time = ride.min_waiting_time; + max_waiting_time = ride.max_waiting_time; + operation_setting = ride.operation_option; + lift_hill_speed = ride.lift_hill_speed; + num_circuits = ride.num_circuits; + + entrance_style = ride.entrance_style; + max_speed = (int8_t)(ride.max_speed / 65536); + average_speed = (int8_t)(ride.average_speed / 65536); + ride_length = ride_get_total_length(&ride) / 65536; + max_positive_vertical_g = ride.max_positive_vertical_g / 32; + max_negative_vertical_g = ride.max_negative_vertical_g / 32; + max_lateral_g = ride.max_lateral_g / 32; + inversions = ride.holes & 0x1F; + inversions = ride.inversions & 0x1F; + inversions |= (ride.sheltered_eighths << 5); + drops = ride.drops; + highest_drop_height = ride.highest_drop_height; + + uint16_t totalAirTime = (ride.total_air_time * 123) / 1024; + if (totalAirTime > 255) + { + totalAirTime = 0; + } + total_air_time = (uint8_t)totalAirTime; + + excitement = ride.ratings.excitement / 10; + intensity = ride.ratings.intensity / 10; + nausea = ride.ratings.nausea / 10; + + upkeep_cost = ride.upkeep_cost; + flags = 0; + flags2 = 0; + + if (type == RIDE_TYPE_MAZE) + { + return CreateTrackDesignMaze(ride); + } + else + { + return CreateTrackDesignTrack(ride); + } +} + +rct_string_id TrackDesign::CreateTrackDesignTrack(const Ride& ride) +{ + CoordsXYE trackElement; + if (!ride_try_get_origin_element(&ride, &trackElement)) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + ride_get_start_of_track(&trackElement); + + int32_t z = trackElement.element->base_height * 8; + uint8_t trackType = trackElement.element->AsTrack()->GetTrackType(); + uint8_t direction = trackElement.element->GetDirection(); + _saveDirection = direction; + + if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, trackType, 0, &trackElement.element, 0)) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + const rct_track_coordinates* trackCoordinates = &TrackCoordinates[trackElement.element->AsTrack()->GetTrackType()]; + // Used in the following loop to know when we have + // completed all of the elements and are back at the + // start. + TileElement* initialMap = trackElement.element; + + int16_t start_x = trackElement.x; + int16_t start_y = trackElement.y; + int16_t start_z = z + trackCoordinates->z_begin; + gTrackPreviewOrigin = { start_x, start_y, start_z }; + + do + { + rct_td6_track_element track{}; + track.type = trackElement.element->AsTrack()->GetTrackType(); + // TODO move to RCT2 limit + if (track.type == TRACK_ELEM_255) + { + track.type = TRACK_ELEM_255_ALIAS; + } + + uint8_t trackFlags; + if (track_element_has_speed_setting(track.type)) + { + trackFlags = trackElement.element->AsTrack()->GetBrakeBoosterSpeed() >> 1; + } + else + { + trackFlags = trackElement.element->AsTrack()->GetSeatRotation(); + } + + if (trackElement.element->AsTrack()->HasChain()) + trackFlags |= (1 << 7); + trackFlags |= trackElement.element->AsTrack()->GetColourScheme() << 4; + if (RideData4[ride.type].flags & RIDE_TYPE_FLAG4_HAS_ALTERNATIVE_TRACK_TYPE + && trackElement.element->AsTrack()->IsInverted()) + { + trackFlags |= TRACK_ELEMENT_FLAG_INVERTED; + } + + track.flags = trackFlags; + track_elements.push_back(track); + + if (!track_block_get_next(&trackElement, &trackElement, nullptr, nullptr)) + { + break; + } + + z = trackElement.element->base_height * 8; + direction = trackElement.element->GetDirection(); + trackType = trackElement.element->AsTrack()->GetTrackType(); + + if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, trackType, 0, &trackElement.element, 0)) + { + break; + } + + // TODO move to RCT2 limit + constexpr auto TD6MaxTrackElements = 8192; + + if (track_elements.size() > TD6MaxTrackElements) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + } while (trackElement.element != initialMap); + + // First entrances, second exits + for (int32_t i = 0; i < 2; i++) + { + for (int32_t station_index = 0; station_index < RCT12_MAX_STATIONS_PER_RIDE; station_index++) + { + z = ride.stations[station_index].Height; + + TileCoordsXYZD location; + if (i == 0) + { + location = ride_get_entrance_location(&ride, station_index); + } + else + { + location = ride_get_exit_location(&ride, station_index); + } + + if (location.isNull()) + { + continue; + } + + int16_t x = location.x * 32; + int16_t y = location.y * 32; + + TileElement* tileElement = map_get_first_element_at(x >> 5, y >> 5); + do + { + if (tileElement == nullptr) + break; + if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) + continue; + if (tileElement->base_height == z) + break; + } while (!(tileElement++)->IsLastForTile()); + + if (tileElement == nullptr) + { + continue; + } + // Add something that stops this from walking off the end + + Direction entranceDirection = tileElement->GetDirection(); + entranceDirection -= _saveDirection; + entranceDirection &= TILE_ELEMENT_DIRECTION_MASK; + + rct_td6_entrance_element entrance{}; + entrance.direction = entranceDirection; + + x -= gTrackPreviewOrigin.x; + y -= gTrackPreviewOrigin.y; + + // Rotate entrance coordinates backwards to the correct direction + rotate_map_coordinates(&x, &y, (0 - _saveDirection) & 3); + entrance.x = x; + entrance.y = y; + + z *= 8; + z -= gTrackPreviewOrigin.z; + z /= 8; + + if (z > 127 || z < -126) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + if (z == 0xFF) + { + z = 0x80; + } + + entrance.z = z; + + // If this is the exit version + if (i == 1) + { + entrance.direction |= (1 << 7); + } + entrance_elements.push_back(entrance); + } + } + + place_virtual_track(this, PTD_OPERATION_DRAW_OUTLINES, true, get_ride(0), 4096, 4096, 0); + + // Resave global vars for scenery reasons. + gTrackPreviewOrigin = { start_x, start_y, start_z }; + + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; + + space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; + space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; + return STR_NONE; +} + +rct_string_id TrackDesign::CreateTrackDesignMaze(const Ride& ride) +{ + auto startLoc = MazeGetFirstElement(ride); + + if (startLoc.element == nullptr) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + gTrackPreviewOrigin = { static_cast(startLoc.x), static_cast(startLoc.y), + (int16_t)(startLoc.element->base_height * 8) }; + + // 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 (int16_t y = startLoc.y, x = startLoc.y; y < 8192; y += 32) + { + for (; x < 8192; x += 32) + { + auto tileElement = map_get_first_element_at(x / 32, y / 32); + do + { + if (tileElement == nullptr) + break; + if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) + continue; + if (tileElement->AsTrack()->GetRideIndex() != ride.id) + continue; + + rct_td6_maze_element maze{}; + + maze.maze_entry = tileElement->AsTrack()->GetMazeEntry(); + maze.x = (x - startLoc.x) / 32; + maze.y = (y - startLoc.y) / 32; + _saveDirection = tileElement->GetDirection(); + maze_elements.push_back(maze); + + if (maze_elements.size() >= 2000) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + } while (!(tileElement++)->IsLastForTile()); + } + x = 0; + } + + auto location = ride_get_entrance_location(&ride, 0); + if (location.isNull()) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + CoordsXY entranceLoc = { location.x * 32, location.y * 32 }; + auto tileElement = map_get_first_element_at(location.x, location.y); + do + { + if (tileElement == nullptr) + break; + if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) + continue; + if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) + continue; + if (tileElement->AsEntrance()->GetRideIndex() == ride.id) + break; + } while (!(tileElement++)->IsLastForTile()); + // Add something that stops this from walking off the end + + uint8_t entranceDirection = tileElement->GetDirection(); + rct_td6_maze_element mazeEntrance{}; + mazeEntrance.direction = entranceDirection; + mazeEntrance.type = 8; + mazeEntrance.x = (int8_t)((entranceLoc.x - startLoc.x) / 32); + mazeEntrance.y = (int8_t)((entranceLoc.y - startLoc.y) / 32); + maze_elements.push_back(mazeEntrance); + + location = ride_get_exit_location(&ride, 0); + if (location.isNull()) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + CoordsXY exitLoc = { location.x * 32, location.y * 32 }; + tileElement = map_get_first_element_at(location.x, location.y); + do + { + if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) + continue; + if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT) + continue; + if (tileElement->AsEntrance()->GetRideIndex() == ride.id) + break; + } while (!(tileElement++)->IsLastForTile()); + // Add something that stops this from walking off the end + + uint8_t exit_direction = tileElement->GetDirection(); + rct_td6_maze_element mazeExit{}; + mazeExit.direction = exit_direction; + mazeExit.type = 0x80; + mazeExit.x = (int8_t)((exitLoc.x - startLoc.x) / 32); + mazeExit.y = (int8_t)((exitLoc.y - startLoc.y) / 32); + maze_elements.push_back(mazeExit); + + // Save global vars as they are still used by scenery???? + int16_t startZ = gTrackPreviewOrigin.z; + place_virtual_track(this, PTD_OPERATION_DRAW_OUTLINES, true, get_ride(0), 4096, 4096, 0); + gTrackPreviewOrigin = { static_cast(startLoc.x), static_cast(startLoc.y), startZ }; + + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; + gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; + + space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; + space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; + return STR_NONE; +} + +CoordsXYE TrackDesign::MazeGetFirstElement(const Ride& ride) +{ + CoordsXYE tile{}; + for (tile.y = 0; tile.y < 8192; tile.y += 32) + { + for (tile.x = 0; tile.x < 8192; tile.x += 32) + { + tile.element = map_get_first_element_at(tile.x / 32, tile.y / 32); + do + { + if (tile.element == nullptr) + break; + + if (tile.element->GetType() != TILE_ELEMENT_TYPE_TRACK) + continue; + if (tile.element->AsTrack()->GetRideIndex() == ride.id) + { + return tile; + } + } while (!(tile.element++)->IsLastForTile()); + } + } + tile.element = nullptr; + return tile; +} + +rct_string_id TrackDesign::CreateTrackDesignScenery() +{ + scenery_elements = _trackSavedTileElementsDesc; + // Run an element loop + for (auto& scenery : scenery_elements) + { + switch (object_entry_get_type(&scenery.scenery_object)) + { + case OBJECT_TYPE_PATHS: + { + uint8_t slope = (scenery.flags & 0x60) >> 5; + slope -= _saveDirection; + + scenery.flags &= 0x9F; + scenery.flags |= ((slope & 3) << 5); + + // Direction of connection on path + uint8_t direction = scenery.flags & 0xF; + // Rotate the direction by the track direction + direction = ((direction << 4) >> _saveDirection); + + scenery.flags &= 0xF0; + scenery.flags |= (direction & 0xF) | (direction >> 4); + break; + } + case OBJECT_TYPE_WALLS: + { + uint8_t direction = scenery.flags & 3; + direction -= _saveDirection; + + scenery.flags &= 0xFC; + scenery.flags |= (direction & 3); + break; + } + default: + { + uint8_t direction = scenery.flags & 3; + uint8_t quadrant = (scenery.flags & 0x0C) >> 2; + + direction -= _saveDirection; + quadrant -= _saveDirection; + + scenery.flags &= 0xF0; + scenery.flags |= (direction & 3) | ((quadrant & 3) << 2); + break; + } + } + + int16_t x = ((uint8_t)scenery.x) * 32 - gTrackPreviewOrigin.x; + int16_t y = ((uint8_t)scenery.y) * 32 - gTrackPreviewOrigin.y; + rotate_map_coordinates(&x, &y, (0 - _saveDirection) & 3); + x /= 32; + y /= 32; + + if (x > 127 || y > 127 || x < -126 || y < -126) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + + scenery.x = (int8_t)x; + scenery.y = (int8_t)y; + + int32_t z = scenery.z * 8 - gTrackPreviewOrigin.z; + z /= 8; + if (z > 127 || z < -126) + { + return STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; + } + scenery.z = z; + } + + return STR_NONE; +} + std::unique_ptr track_design_open(const utf8* path) { auto trackImporter = TrackImporter::Create(path); trackImporter->Load(path); try { - auto track2 = trackImporter->Import(); - log_error("Test %d", track2->type); - return track2; + return trackImporter->Import(); } catch (const std::exception& e) { diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index 1a915a88d9..f78bc4f44d 100644 --- a/src/openrct2/ride/TrackDesign.h +++ b/src/openrct2/ride/TrackDesign.h @@ -218,6 +218,16 @@ struct TrackDesign std::vector scenery_elements; std::unique_ptr name; + +public: + rct_string_id CreateTrackDesign(const Ride& ride); + rct_string_id CreateTrackDesignScenery(); + +private: + uint8_t _saveDirection; + rct_string_id CreateTrackDesignTrack(const Ride& ride); + rct_string_id CreateTrackDesignMaze(const Ride& ride); + CoordsXYE MazeGetFirstElement(const Ride& ride); }; // Only written to in RCT2, not used in OpenRCT2. All of these are elements that had to be invented in RCT1.