diff --git a/src/openrct2/core/DataSerialiserTraits.h b/src/openrct2/core/DataSerialiserTraits.h index ca391d360c..a878a86ab9 100644 --- a/src/openrct2/core/DataSerialiserTraits.h +++ b/src/openrct2/core/DataSerialiserTraits.h @@ -598,6 +598,50 @@ template<> struct DataSerializerTraits_t } }; +template<> struct DataSerializerTraits_t +{ + static void encode(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val) + { + stream->WriteValue(static_cast(val.Generation)); + if (val.Generation == ObjectGeneration::DAT) + { + DataSerializerTraits s; + s.encode(stream, val.Entry); + } + else + { + stream->WriteValue(static_cast(val.GetType())); + stream->WriteString(val.Identifier); + } + } + + static void decode(OpenRCT2::IStream* stream, ObjectEntryDescriptor& val) + { + auto generation = static_cast(stream->ReadValue()); + if (generation == ObjectGeneration::DAT) + { + DataSerializerTraits s; + rct_object_entry entry; + s.decode(stream, entry); + val = ObjectEntryDescriptor(entry); + } + else + { + auto type = static_cast(stream->ReadValue()); + auto identifier = stream->ReadStdString(); + val = ObjectEntryDescriptor(type, identifier); + } + } + + static void log(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val) + { + auto identifier = std::string(val.GetName()); + char msg[128] = {}; + snprintf(msg, sizeof(msg), "ObjectEntryDescriptor[%s]", identifier.c_str()); + stream->Write(msg, strlen(msg)); + } +}; + template<> struct DataSerializerTraits_t { static void encode(OpenRCT2::IStream* stream, const TrackDesignTrackElement& val) @@ -677,7 +721,7 @@ template<> struct DataSerializerTraits_t stream->Write(&val.flags); stream->Write(&val.primary_colour); stream->Write(&val.secondary_colour); - DataSerializerTraits s; + DataSerializerTraits s; s.encode(stream, val.scenery_object); } static void decode(OpenRCT2::IStream* stream, TrackDesignSceneryElement& val) @@ -688,7 +732,7 @@ template<> struct DataSerializerTraits_t stream->Read(&val.flags); stream->Read(&val.primary_colour); stream->Read(&val.secondary_colour); - DataSerializerTraits s; + DataSerializerTraits s; s.decode(stream, val.scenery_object); } static void log(OpenRCT2::IStream* stream, const TrackDesignSceneryElement& val) @@ -698,7 +742,9 @@ template<> struct DataSerializerTraits_t msg, sizeof(msg), "TrackDesignSceneryElement(x = %d, y = %d, z = %d, flags = %d, colour1 = %d, colour2 = %d)", val.x, val.y, val.z, val.flags, val.primary_colour, val.secondary_colour); stream->Write(msg, strlen(msg)); - stream->WriteArray(val.scenery_object.name, 8); + + auto identifier = val.scenery_object.GetName(); + stream->WriteArray(identifier.data(), identifier.size()); } }; @@ -722,49 +768,6 @@ template<> struct DataSerializerTraits_t } }; -template<> struct DataSerializerTraits_t -{ - static void encode(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val) - { - stream->Write(&val.Generation); - if (val.Generation == ObjectGeneration::DAT) - { - DataSerializerTraits s; - s.encode(stream, val.Entry); - } - else - { - DataSerializerTraits s; - s.encode(stream, val.Identifier); - } - } - static void decode(OpenRCT2::IStream* stream, ObjectEntryDescriptor& val) - { - ObjectGeneration generation; - stream->Read(&generation); - if (generation == ObjectGeneration::DAT) - { - rct_object_entry obj; - DataSerializerTraits s; - s.decode(stream, obj); - val = ObjectEntryDescriptor(obj); - } - else - { - std::string id; - DataSerializerTraits s; - s.decode(stream, id); - val = ObjectEntryDescriptor(id); - } - } - static void log(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val) - { - char msg[128] = {}; - snprintf(msg, sizeof(msg), "ObjectEntryDescriptor (Generation = %d)", static_cast(val.Generation)); - stream->Write(msg, strlen(msg)); - } -}; - template<> struct DataSerializerTraits_t { static void encode(OpenRCT2::IStream* stream, const IntensityRange& val) diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 67e5a98bfd..e122424fe2 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3913,6 +3913,8 @@ enum STR_FOLLOW_SUBJECT_TIP = 6458, + STR_UNSUPPORTED_OBJECT_FORMAT = 6459, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working /* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings }; diff --git a/src/openrct2/rct12/RCT12.cpp b/src/openrct2/rct12/RCT12.cpp index 08de6fab9f..4a387f6ea9 100644 --- a/src/openrct2/rct12/RCT12.cpp +++ b/src/openrct2/rct12/RCT12.cpp @@ -1347,21 +1347,12 @@ RCT12TrackType OpenRCT2FlatTrackTypeToRCT12(track_type_t origTrackType) return origTrackType; } -money64 RCT12CompletedCompanyValueToOpenRCT2(money32 origValue) -{ - if (origValue == RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE) - return COMPANY_VALUE_ON_FAILED_OBJECTIVE; - - return ToMoney64(origValue); -} - -money32 OpenRCT2CompletedCompanyValueToRCT12(money64 origValue) -{ - if (origValue == COMPANY_VALUE_ON_FAILED_OBJECTIVE) - return RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE; - - return ToMoney32(origValue); -} +static constexpr std::string_view _stationStyles[] = { + "rct2.station.plain", "rct2.station.wooden", "rct2.station.canvas_tent", "rct2.station.castle_grey", + "rct2.station.castle_brown", "rct2.station.jungle", "rct2.station.log", "rct2.station.classical", + "rct2.station.abstract", "rct2.station.snow", "rct2.station.pagoda", "rct2.station.space", + "openrct2.station.noentrance" +}; static constexpr std::string_view _musicStyles[] = { "rct2.music.dodgems", @@ -1399,6 +1390,15 @@ static constexpr std::string_view _musicStyles[] = { "rct2.music.candy", }; +std::string_view GetStationIdentifierFromStyle(uint8_t style) +{ + if (style < std::size(_stationStyles)) + { + return _stationStyles[style]; + } + return {}; +} + std::optional GetStyleFromMusicIdentifier(std::string_view identifier) { auto it = std::find(std::begin(_musicStyles), std::end(_musicStyles), identifier); @@ -1408,3 +1408,19 @@ std::optional GetStyleFromMusicIdentifier(std::string_view identifier) } return std::nullopt; } + +money64 RCT12CompletedCompanyValueToOpenRCT2(money32 origValue) +{ + if (origValue == RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE) + return COMPANY_VALUE_ON_FAILED_OBJECTIVE; + + return ToMoney64(origValue); +} + +money32 OpenRCT2CompletedCompanyValueToRCT12(money64 origValue) +{ + if (origValue == COMPANY_VALUE_ON_FAILED_OBJECTIVE) + return RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE; + + return ToMoney32(origValue); +} diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h index 9507cb1c90..3e23bc95c7 100644 --- a/src/openrct2/rct12/RCT12.h +++ b/src/openrct2/rct12/RCT12.h @@ -19,6 +19,8 @@ #include #include +class ObjectList; + using track_type_t = uint16_t; using RCT12TrackType = uint8_t; @@ -940,6 +942,7 @@ std::string ConvertFormattedStringToRCT2(std::string_view buffer, size_t maxLeng std::string GetTruncatedRCT2String(std::string_view src, size_t maxLength); track_type_t RCT12FlatTrackTypeToOpenRCT2(RCT12TrackType origTrackType); RCT12TrackType OpenRCT2FlatTrackTypeToRCT12(track_type_t origTrackType); +std::string_view GetStationIdentifierFromStyle(uint8_t style); std::optional GetStyleFromMusicIdentifier(std::string_view identifier); static constexpr money32 RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE = 0x80000001; diff --git a/src/openrct2/rct2/RCT2.cpp b/src/openrct2/rct2/RCT2.cpp index 2fba4a88c6..b20d609a04 100644 --- a/src/openrct2/rct2/RCT2.cpp +++ b/src/openrct2/rct2/RCT2.cpp @@ -7,7 +7,9 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ +#include "../Context.h" #include "../object/Object.h" +#include "../object/ObjectManager.h" #include "../ride/Ride.h" #include "../ride/RideData.h" #include "../ride/Track.h" @@ -156,3 +158,107 @@ RCT12TrackType OpenRCT2TrackTypeToRCT2(track_type_t origTrackType) // This function is safe to run this way round. return OpenRCT2FlatTrackTypeToRCT12(origTrackType); } + +static FootpathMapping _footpathMappings[] = { + // RCT2 mappings + { "PATHASH ", "rct2.footpath_surface.ash", "rct2.footpath_surface.queue_yellow", "rct2.footpath_railings.bamboo_black" }, + { "PATHCRZY", "rct2.footpath_surface.crazy_paving", "rct2.footpath_surface.queue_yellow", + "rct2.footpath_railings.concrete" }, + { "PATHDIRT", "rct2.footpath_surface.dirt", "rct2.footpath_surface.queue_yellow", "rct2.footpath_railings.bamboo_brown" }, + { "PATHSPCE", "rct2.footpath_surface.tarmac_red", "rct2.footpath_surface.queue_red", "rct2.footpath_railings.space" }, + { "ROAD ", "rct2.footpath_surface.road", "rct2.footpath_surface.queue_blue", "rct2.footpath_railings.wood" }, + { "TARMACB ", "rct2.footpath_surface.tarmac_brown", "rct2.footpath_surface.queue_yellow", + "rct2.footpath_railings.concrete" }, + { "TARMACG ", "rct2.footpath_surface.tarmac_green", "rct2.footpath_surface.queue_green", + "rct2.footpath_railings.concrete_green" }, + { "TARMAC ", "rct2.footpath_surface.tarmac", "rct2.footpath_surface.queue_blue", "rct2.footpath_railings.wood" }, + // Time Twister + { "1920PATH", "rct2tt.footpath_surface.pavement", "rct2tt.footpath_surface.queue_pavement", + "rct2tt.footpath_railings.pavement" }, + { "FUTRPATH", "rct2tt.footpath_surface.circuitboard", "rct2tt.footpath_surface.queue_circuitboard", + "rct2tt.footpath_railings.circuitboard" }, + { "FUTRPAT2", "rct2tt.footpath_surface.circuitboard", "rct2tt.footpath_surface.queue_circuitboard", + "openrct2.footpath_railings.invisible" }, + { "JURRPATH", "rct2tt.footpath_surface.rocky", "rct2.footpath_surface.queue_yellow", "rct2tt.footpath_railings.rocky" }, + { "MEDIPATH", "rct2tt.footpath_surface.medieval", "rct2.footpath_surface.queue_yellow", + "rct2tt.footpath_railings.medieval" }, + { "MYTHPATH", "rct2tt.footpath_surface.mosaic", "rct2.footpath_surface.queue_yellow", + "rct2tt.footpath_railings.balustrade" }, + { "RANBPATH", "rct2tt.footpath_surface.rainbow", "rct2tt.footpath_surface.queue_rainbow", + "rct2tt.footpath_railings.rainbow" }, + + // RCT 1 mappings (for reverse lookup) + { "PATHASH ", "rct1aa.footpath_surface.ash", "rct1aa.footpath_surface.queue_yellow", + "rct2.footpath_railings.bamboo_black" }, + { "PATHCRZY", "rct1.footpath_surface.crazy_paving", "rct1aa.footpath_surface.queue_yellow", + "rct2.footpath_railings.concrete" }, + { "PATHDIRT", "rct1.footpath_surface.dirt", "rct1aa.footpath_surface.queue_yellow", "rct2.footpath_railings.bamboo_brown" }, + { "PATHSPCE", "rct1aa.footpath_surface.tarmac_red", "rct1.footpath_surface.queue_red", "rct1ll.footpath_railings.space" }, + { "TARMACB ", "rct1aa.footpath_surface.tarmac_brown", "rct1aa.footpath_surface.queue_yellow", + "rct2.footpath_railings.concrete" }, + { "TARMACG ", "rct1aa.footpath_surface.tarmac_green", "rct1aa.footpath_surface.queue_green", + "rct2.footpath_railings.concrete_green" }, + { "TARMAC ", "rct1.footpath_surface.tarmac", "rct1.footpath_surface.queue_blue", "rct2.footpath_railings.wood" }, + { "PATHCRZY", "rct1.footpath_surface.tiles_brown", "rct1aa.footpath_surface.queue_yellow", + "rct2.footpath_railings.concrete" }, + { "PATHCRZY", "rct1aa.footpath_surface.tiles_grey", "rct1.footpath_surface.queue_blue", "rct2.footpath_railings.concrete" }, + { "PATHCRZY", "rct1ll.footpath_surface.tiles_red", "rct1.footpath_surface.queue_red", "rct2.footpath_railings.concrete" }, + { "PATHCRZY", "rct1ll.footpath_surface.tiles_green", "rct1aa.footpath_surface.queue_green", + "rct2.footpath_railings.concrete" }, +}; + +const FootpathMapping* GetFootpathSurfaceId(const ObjectEntryDescriptor& desc, bool ideallyLoaded, bool isQueue) +{ + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + + auto name = desc.Entry.GetName(); + for (const auto& mapping : _footpathMappings) + { + if (mapping.Original == name) + { + if (ideallyLoaded) + { + auto obj = objManager.GetLoadedObject( + ObjectEntryDescriptor(isQueue ? mapping.QueueSurface : mapping.NormalSurface)); + if (obj == nullptr) + continue; + } + return &mapping; + } + } + return nullptr; +} + +std::optional GetBestObjectEntryForSurface(std::string_view surface, std::string_view railings) +{ + rct_object_entry result; + std::memset(&result, 0, sizeof(result)); + + result.SetType(ObjectType::Paths); + + auto foundMapping = false; + for (const auto& mapping : _footpathMappings) + { + if (surface == mapping.NormalSurface || surface == mapping.QueueSurface) + { + if (railings == mapping.Railing) + { + // Best match found + foundMapping = true; + result.SetName(mapping.Original); + break; + } + + if (!foundMapping) + { + // Found a mapping, but keep searching to see if there is a closer match + foundMapping = true; + result.SetName(mapping.Original); + } + } + } + + if (foundMapping) + return result; + return {}; +} diff --git a/src/openrct2/rct2/RCT2.h b/src/openrct2/rct2/RCT2.h index 22575fe640..d571ec96d6 100644 --- a/src/openrct2/rct2/RCT2.h +++ b/src/openrct2/rct2/RCT2.h @@ -1077,3 +1077,15 @@ RCT12TrackType OpenRCT2TrackTypeToRCT2(track_type_t origTrackType); * Handles single and multi-byte strings. */ size_t GetRCT2StringBufferLen(const char* buffer, size_t maxBufferLen); + +struct FootpathMapping +{ + std::string_view Original; + std::string_view NormalSurface; + std::string_view QueueSurface; + std::string_view Railing; +}; + +const FootpathMapping* GetFootpathSurfaceId( + const ObjectEntryDescriptor& desc, bool ideallyLoaded = false, bool isQueue = false); +std::optional GetBestObjectEntryForSurface(std::string_view surface, std::string_view railings); diff --git a/src/openrct2/rct2/T6Exporter.cpp b/src/openrct2/rct2/T6Exporter.cpp index 292e410f77..33554c5a83 100644 --- a/src/openrct2/rct2/T6Exporter.cpp +++ b/src/openrct2/rct2/T6Exporter.cpp @@ -80,7 +80,7 @@ bool T6Exporter::SaveTrack(OpenRCT2::IStream* stream) 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.Write(&_trackDesign->vehicle_object.Entry, 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); @@ -123,7 +123,7 @@ bool T6Exporter::SaveTrack(OpenRCT2::IStream* stream) for (const auto& sceneryElement : _trackDesign->scenery_elements) { - tempStream.Write(&sceneryElement.scenery_object, sizeof(rct_object_entry)); + tempStream.Write(&sceneryElement.scenery_object.Entry, sizeof(rct_object_entry)); tempStream.WriteValue(sceneryElement.x); tempStream.WriteValue(sceneryElement.y); tempStream.WriteValue(sceneryElement.z); diff --git a/src/openrct2/rct2/T6Importer.cpp b/src/openrct2/rct2/T6Importer.cpp index 55794068e4..a32037b4c9 100644 --- a/src/openrct2/rct2/T6Importer.cpp +++ b/src/openrct2/rct2/T6Importer.cpp @@ -200,7 +200,7 @@ public: rct_td6_scenery_element t6SceneryElement{}; _stream.Read(&t6SceneryElement, sizeof(rct_td6_scenery_element)); TrackDesignSceneryElement sceneryElement{}; - sceneryElement.scenery_object = t6SceneryElement.scenery_object; + sceneryElement.scenery_object = ObjectEntryDescriptor(t6SceneryElement.scenery_object); sceneryElement.x = t6SceneryElement.x; sceneryElement.y = t6SceneryElement.y; sceneryElement.z = t6SceneryElement.z; diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 0a816bf473..55317043d2 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -41,6 +41,7 @@ #include "../management/Finance.h" #include "../network/network.h" #include "../object/FootpathObject.h" +#include "../object/FootpathSurfaceObject.h" #include "../object/ObjectList.h" #include "../object/ObjectManager.h" #include "../object/ObjectRepository.h" @@ -648,37 +649,133 @@ static void track_design_load_scenery_objects(TrackDesign* td6) // Load scenery objects for (const auto& scenery : td6->scenery_elements) { - const rct_object_entry* sceneryEntry = &scenery.scenery_object; - object_manager_load_object(sceneryEntry); + if (scenery.scenery_object.HasValue()) + { + objectManager.LoadObject(td6->vehicle_object); + } } } +struct TrackSceneryEntry +{ + ObjectType Type = ObjectType::None; + ObjectEntryIndex Index = OBJECT_ENTRY_INDEX_NULL; + ObjectEntryIndex SecondaryIndex = OBJECT_ENTRY_INDEX_NULL; // For footpath railing +}; + +static ObjectEntryIndex TrackDesignGetDefaultSurfaceIndex(bool isQueue) +{ + for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_SURFACE_OBJECTS; i++) + { + auto footpathSurfaceObj = GetPathSurfaceEntry(i); + if (footpathSurfaceObj != nullptr) + { + if (footpathSurfaceObj->Flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) + { + continue; + } + + if (isQueue != ((footpathSurfaceObj->Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0)) + { + continue; + } + + return i; + } + } + return OBJECT_ENTRY_INDEX_NULL; +} + +static ObjectEntryIndex TrackDesignGetDefaultRailingIndex() +{ + for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_RAILINGS_OBJECTS; i++) + { + auto footpathRailingsObj = GetPathRailingsEntry(i); + if (footpathRailingsObj != nullptr) + { + return i; + } + } + return OBJECT_ENTRY_INDEX_NULL; +} + +static std::optional TrackDesignPlaceSceneryElementGetEntry(const TrackDesignSceneryElement& scenery) +{ + TrackSceneryEntry result; + + auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); + if (scenery.scenery_object.GetType() == ObjectType::Paths) + { + auto footpathMapping = GetFootpathSurfaceId(scenery.scenery_object, true, scenery.IsQueue()); + if (footpathMapping == nullptr) + { + // Check if legacy path object is loaded + auto obj = objectMgr.GetLoadedObject(scenery.scenery_object); + if (obj != nullptr) + { + result.Type = obj->GetObjectType(); + result.Index = objectMgr.GetLoadedObjectEntryIndex(obj); + } + else + { + result.Type = ObjectType::FootpathSurface; + } + } + else + { + result.Type = ObjectType::FootpathSurface; + result.Index = objectMgr.GetLoadedObjectEntryIndex( + ObjectEntryDescriptor(scenery.IsQueue() ? footpathMapping->QueueSurface : footpathMapping->NormalSurface)); + result.SecondaryIndex = objectMgr.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(footpathMapping->Railing)); + } + + if (result.Index == OBJECT_ENTRY_INDEX_NULL) + result.Index = TrackDesignGetDefaultSurfaceIndex(scenery.IsQueue()); + if (result.SecondaryIndex == OBJECT_ENTRY_INDEX_NULL) + result.SecondaryIndex = TrackDesignGetDefaultRailingIndex(); + + if (result.Index == OBJECT_ENTRY_INDEX_NULL || result.SecondaryIndex == OBJECT_ENTRY_INDEX_NULL) + { + _trackDesignPlaceStateSceneryUnavailable = true; + return {}; + } + } + else + { + auto obj = objectMgr.GetLoadedObject(scenery.scenery_object); + if (obj != nullptr) + { + result.Type = obj->GetObjectType(); + result.Index = objectMgr.GetLoadedObjectEntryIndex(obj); + } + else + { + _trackDesignPlaceStateSceneryUnavailable = true; + return {}; + } + } + return result; +} + /** * * rct2: 0x006D247A */ static void track_design_mirror_scenery(TrackDesign* td6) { + auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); for (auto& scenery : td6->scenery_elements) { - ObjectType entry_type{ 0 }; - ObjectEntryIndex entryIndex{ 0 }; - if (!find_object_in_entry_group(&scenery.scenery_object, &entry_type, &entryIndex)) - { - entry_type = scenery.scenery_object.GetType(); - if (entry_type != ObjectType::Paths) - { - continue; - } + auto entryInfo = TrackDesignPlaceSceneryElementGetEntry(scenery); + if (!entryInfo) + continue; - entryIndex = 0; - } - - switch (entry_type) + auto obj = objectMgr.GetLoadedObject(entryInfo->Type, entryInfo->Index); + switch (obj->GetObjectType()) { case ObjectType::LargeScenery: { - auto* sceneryEntry = static_cast(object_entry_get_chunk(entry_type, entryIndex)); + auto* sceneryEntry = reinterpret_cast(obj->GetLegacyData()); int16_t x1 = 0, x2 = 0, y1 = 0, y2 = 0; for (rct_large_scenery_tile* tile = sceneryEntry->tiles; tile->x_offset != -1; tile++) { @@ -723,7 +820,7 @@ static void track_design_mirror_scenery(TrackDesign* td6) } case ObjectType::SmallScenery: { - auto* sceneryEntry = static_cast(object_entry_get_chunk(entry_type, entryIndex)); + auto* sceneryEntry = reinterpret_cast(obj->GetLegacyData()); scenery.y = -scenery.y; if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_DIAGONAL)) @@ -753,6 +850,7 @@ static void track_design_mirror_scenery(TrackDesign* td6) break; } case ObjectType::Paths: + case ObjectType::FootpathSurface: { scenery.y = -scenery.y; @@ -768,7 +866,6 @@ static void track_design_mirror_scenery(TrackDesign* td6) break; } default: - // This switch processes only ObjectType for Scenery items. break; } } @@ -865,54 +962,11 @@ static void track_design_update_max_min_coordinates(const CoordsXYZ& coords) std::max(_trackPreviewMax.z, coords.z) }; } -static bool TrackDesignPlaceSceneryElementGetEntry( - ObjectType& entry_type, ObjectEntryIndex& entry_index, const TrackDesignSceneryElement& scenery) -{ - if (!find_object_in_entry_group(&scenery.scenery_object, &entry_type, &entry_index)) - { - entry_type = scenery.scenery_object.GetType(); - if (entry_type != ObjectType::Paths) - { - _trackDesignPlaceStateSceneryUnavailable = true; - return true; - } - - if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) - { - _trackDesignPlaceStateSceneryUnavailable = true; - return true; - } - - entry_index = 0; - for (; entry_index < object_entry_group_counts[EnumValue(ObjectType::Paths)]; entry_index++) - { - const auto* legacyPathEntry = GetLegacyFootpathEntry(entry_index); - if (legacyPathEntry == nullptr) - { - return true; - } - const auto& surfaceDescriptor = legacyPathEntry->GetPathSurfaceDescriptor(); - if (surfaceDescriptor.IsEditorOnly()) - { - return true; - } - } - - if (entry_index == object_entry_group_counts[EnumValue(ObjectType::Paths)]) - { - _trackDesignPlaceStateSceneryUnavailable = true; - return true; - } - } - return false; -} - static bool TrackDesignPlaceSceneryElementRemoveGhost( CoordsXY mapCoord, const TrackDesignSceneryElement& scenery, uint8_t rotation, int32_t originZ) { - ObjectType entry_type; - ObjectEntryIndex entry_index; - if (TrackDesignPlaceSceneryElementGetEntry(entry_type, entry_index, scenery)) + auto entryInfo = TrackDesignPlaceSceneryElementGetEntry(scenery); + if (!entryInfo) { return true; } @@ -927,14 +981,14 @@ static bool TrackDesignPlaceSceneryElementRemoveGhost( const uint32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; std::unique_ptr ga; - switch (entry_type) + switch (entryInfo->Type) { case ObjectType::SmallScenery: { uint8_t quadrant = (scenery.flags >> 2) + _currentTrackPieceDirection; quadrant &= 3; - auto* sceneryEntry = get_small_scenery_entry(entry_index); + auto* sceneryEntry = get_small_scenery_entry(entryInfo->Index); if (!(!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_DIAGONAL)) && sceneryEntry->HasFlag( SMALL_SCENERY_FLAG_DIAGONAL | SMALL_SCENERY_FLAG_HALF_SPACE | SMALL_SCENERY_FLAG_THREE_QUARTERS)) @@ -942,7 +996,7 @@ static bool TrackDesignPlaceSceneryElementRemoveGhost( quadrant = 0; } - ga = std::make_unique(CoordsXYZ{ mapCoord.x, mapCoord.y, z }, quadrant, entry_index); + ga = std::make_unique(CoordsXYZ{ mapCoord.x, mapCoord.y, z }, quadrant, entryInfo->Index); break; } case ObjectType::LargeScenery: @@ -952,6 +1006,7 @@ static bool TrackDesignPlaceSceneryElementRemoveGhost( ga = std::make_unique(CoordsXYZD{ mapCoord.x, mapCoord.y, z, sceneryRotation }); break; case ObjectType::Paths: + case ObjectType::FootpathSurface: ga = std::make_unique(CoordsXYZ{ mapCoord.x, mapCoord.y, z }); break; default: @@ -970,10 +1025,7 @@ static bool TrackDesignPlaceSceneryElementGetPlaceZ(const TrackDesignSceneryElem _trackDesignPlaceSceneryZ = z; } - ObjectType entry_type; - ObjectEntryIndex entry_index; - TrackDesignPlaceSceneryElementGetEntry(entry_type, entry_index, scenery); - + TrackDesignPlaceSceneryElementGetEntry(scenery); return true; } @@ -1007,9 +1059,8 @@ static std::optional TrackDesignPlaceSceneryElement( || _trackDesignPlaceOperation == PTD_OPERATION_PLACE_GHOST || _trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) { - ObjectType entry_type; - ObjectEntryIndex entry_index; - if (TrackDesignPlaceSceneryElementGetEntry(entry_type, entry_index, scenery)) + auto entryInfo = TrackDesignPlaceSceneryElementGetEntry(scenery); + if (!entryInfo) { return 0; } @@ -1019,7 +1070,7 @@ static std::optional TrackDesignPlaceSceneryElement( uint8_t flags; uint8_t quadrant; - switch (entry_type) + switch (entryInfo->Type) { case ObjectType::SmallScenery: { @@ -1059,7 +1110,7 @@ static std::optional TrackDesignPlaceSceneryElement( gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE; auto smallSceneryPlace = SmallSceneryPlaceAction( - { mapCoord.x, mapCoord.y, z, rotation }, quadrant, entry_index, scenery.primary_colour, + { mapCoord.x, mapCoord.y, z, rotation }, quadrant, entryInfo->Index, scenery.primary_colour, scenery.secondary_colour); smallSceneryPlace.SetFlags(flags); @@ -1105,7 +1156,8 @@ static std::optional TrackDesignPlaceSceneryElement( flags |= GAME_COMMAND_FLAG_REPLAY; } auto sceneryPlaceAction = LargeSceneryPlaceAction( - { mapCoord.x, mapCoord.y, z, rotation }, entry_index, scenery.primary_colour, scenery.secondary_colour); + { mapCoord.x, mapCoord.y, z, rotation }, entryInfo->Index, scenery.primary_colour, + scenery.secondary_colour); sceneryPlaceAction.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&sceneryPlaceAction) : GameActions::QueryNested(&sceneryPlaceAction); @@ -1148,7 +1200,7 @@ static std::optional TrackDesignPlaceSceneryElement( flags |= GAME_COMMAND_FLAG_REPLAY; } auto wallPlaceAction = WallPlaceAction( - entry_index, { mapCoord.x, mapCoord.y, z }, rotation, scenery.primary_colour, scenery.secondary_colour, + entryInfo->Index, { mapCoord.x, mapCoord.y, z }, rotation, scenery.primary_colour, scenery.secondary_colour, (scenery.flags & 0xFC) >> 2); wallPlaceAction.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&wallPlaceAction) @@ -1158,6 +1210,7 @@ static std::optional TrackDesignPlaceSceneryElement( break; } case ObjectType::Paths: + case ObjectType::FootpathSurface: if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z) { return 0; @@ -1196,12 +1249,14 @@ static std::optional TrackDesignPlaceSceneryElement( } uint8_t slope = ((bh >> 5) & 0x3) | ((bh >> 2) & 0x4); uint8_t edges = bh & 0xF; - PathConstructFlags constructFlags = PathConstructFlag::IsLegacyPathObject; + PathConstructFlags constructFlags = 0; if (isQueue) constructFlags |= PathConstructFlag::IsQueue; + if (entryInfo->Type == ObjectType::Paths) + constructFlags |= PathConstructFlag::IsLegacyPathObject; auto footpathPlaceAction = FootpathPlaceFromTrackAction( - { mapCoord.x, mapCoord.y, z * COORDS_Z_STEP }, slope, entry_index, OBJECT_ENTRY_INDEX_NULL, edges, - constructFlags); + { mapCoord.x, mapCoord.y, z * COORDS_Z_STEP }, slope, entryInfo->Index, entryInfo->SecondaryIndex, + edges, constructFlags); footpathPlaceAction.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&footpathPlaceAction) : GameActions::QueryNested(&footpathPlaceAction); @@ -1903,7 +1958,13 @@ static bool track_design_place_preview(TrackDesign* td6, money32* cost, Ride** o return false; ride->custom_name = {}; - ride->entrance_style = td6->entrance_style; + + auto stationIdentifier = GetStationIdentifierFromStyle(td6->entrance_style); + ride->entrance_style = objManager.GetLoadedObjectEntryIndex(stationIdentifier); + if (ride->entrance_style == OBJECT_ENTRY_INDEX_NULL) + { + ride->entrance_style = gLastEntranceStyle; + } for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) { diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index 4bbfd49ed5..eabc486918 100644 --- a/src/openrct2/ride/TrackDesign.h +++ b/src/openrct2/ride/TrackDesign.h @@ -32,13 +32,13 @@ struct TrackDesignEntranceElement /* Track Scenery entry size: 0x16 */ struct TrackDesignSceneryElement { - rct_object_entry scenery_object; // 0x00 - int8_t x; // 0x10 - int8_t y; // 0x11 - int8_t z; // 0x12 - uint8_t flags; // 0x13 direction quadrant tertiary colour - uint8_t primary_colour; // 0x14 - uint8_t secondary_colour; // 0x15 + ObjectEntryDescriptor scenery_object; + int8_t x; + int8_t y; + int8_t z; + uint8_t flags; + uint8_t primary_colour; + uint8_t secondary_colour; bool IsQueue() const { diff --git a/src/openrct2/ride/TrackDesignSave.cpp b/src/openrct2/ride/TrackDesignSave.cpp index e342d72d23..90d5cbfb3c 100644 --- a/src/openrct2/ride/TrackDesignSave.cpp +++ b/src/openrct2/ride/TrackDesignSave.cpp @@ -14,7 +14,12 @@ #include "../interface/Viewport.h" #include "../localisation/Localisation.h" #include "../localisation/StringIds.h" +#include "../object/FootpathObject.h" +#include "../object/FootpathRailingsObject.h" +#include "../object/FootpathSurfaceObject.h" +#include "../object/LargeSceneryObject.h" #include "../object/ObjectList.h" +#include "../object/ObjectManager.h" #include "../util/SawyerCoding.h" #include "../util/Util.h" #include "../windows/Intent.h" @@ -41,9 +46,25 @@ ride_id_t gTrackDesignSaveRideIndex = RIDE_ID_NULL; std::vector _trackSavedTileElements; std::vector _trackSavedTileElementsDesc; +struct TrackDesignAddStatus +{ + bool IsSuccess{}; + rct_string_id Message{}; + + static TrackDesignAddStatus Success() + { + return { true, rct_string_id() }; + } + + static TrackDesignAddStatus Fail(rct_string_id message) + { + return { false, message }; + } +}; + static bool track_design_save_should_select_scenery_around(ride_id_t rideIndex, TileElement* tileElement); static void track_design_save_select_nearby_scenery_for_tile(ride_id_t rideIndex, int32_t cx, int32_t cy); -static bool track_design_save_add_tile_element( +static TrackDesignAddStatus track_design_save_add_tile_element( ViewportInteractionItem interactionType, const CoordsXY& loc, TileElement* tileElement); static void track_design_save_remove_tile_element( ViewportInteractionItem interactionType, const CoordsXY& loc, TileElement* tileElement); @@ -72,11 +93,10 @@ void track_design_save_select_tile_element( { if (collect) { - if (!track_design_save_add_tile_element(interactionType, loc, tileElement)) + auto result = track_design_save_add_tile_element(interactionType, loc, tileElement); + if (!result.IsSuccess) { - context_show_error( - STR_SAVE_TRACK_SCENERY_UNABLE_TO_SELECT_ADDITIONAL_ITEM_OF_SCENERY, - STR_SAVE_TRACK_SCENERY_TOO_MANY_ITEMS_SELECTED, {}); + context_show_error(STR_SAVE_TRACK_SCENERY_UNABLE_TO_SELECT_ADDITIONAL_ITEM_OF_SCENERY, result.Message, {}); } } } @@ -188,16 +208,18 @@ static void track_design_save_push_tile_element(const CoordsXY& loc, TileElement } } -/** - * - * rct2: 0x006D2FA7 - */ +static bool track_design_is_supported_object(const Object* obj) +{ + const auto& entry = obj->GetObjectEntry(); + return !entry.IsEmpty(); +} + static void track_design_save_push_tile_element_desc( const rct_object_entry& entry, const CoordsXYZ& loc, uint8_t flags, uint8_t primaryColour, uint8_t secondaryColour) { auto tileLoc = TileCoordsXYZ(loc); TrackDesignSceneryElement item{}; - item.scenery_object = entry; + item.scenery_object = ObjectEntryDescriptor(entry); item.x = tileLoc.x; item.y = tileLoc.y; item.z = tileLoc.z; @@ -208,96 +230,146 @@ static void track_design_save_push_tile_element_desc( _trackSavedTileElementsDesc.push_back(std::move(item)); } -static void track_design_save_add_scenery(const CoordsXY& loc, SmallSceneryElement* sceneryElement) +static void track_design_save_push_tile_element_desc( + const Object* obj, const CoordsXYZ& loc, uint8_t flags, uint8_t primaryColour, uint8_t secondaryColour) { - int32_t entryType = sceneryElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::SmallScenery, entryType); + const auto& entry = obj->GetObjectEntry(); + if (entry.IsEmpty()) + { + // Unsupported, should have been blocked earlier + assert(false); + } - uint8_t flags = 0; - flags |= sceneryElement->GetDirection(); - flags |= sceneryElement->GetSceneryQuadrant() << 2; - - uint8_t primaryColour = sceneryElement->GetPrimaryColour(); - uint8_t secondaryColour = sceneryElement->GetSecondaryColour(); - - track_design_save_push_tile_element(loc, reinterpret_cast(sceneryElement)); - track_design_save_push_tile_element_desc( - entry->GetObjectEntry(), { loc.x, loc.y, sceneryElement->GetBaseZ() }, flags, primaryColour, secondaryColour); + track_design_save_push_tile_element_desc(entry, loc, flags, primaryColour, secondaryColour); } -static void track_design_save_add_large_scenery(const CoordsXY& loc, LargeSceneryElement* tileElement) +static TrackDesignAddStatus track_design_save_add_scenery(const CoordsXY& loc, SmallSceneryElement* sceneryElement) { - rct_large_scenery_tile *sceneryTiles, *tile; - int32_t direction, sequence; - - if (tileElement == nullptr) + auto entryIndex = sceneryElement->GetEntryIndex(); + auto obj = object_entry_get_object(ObjectType::SmallScenery, entryIndex); + if (obj != nullptr && track_design_is_supported_object(obj)) { - log_warning("Null tile element"); - return; + uint8_t flags = 0; + flags |= sceneryElement->GetDirection(); + flags |= sceneryElement->GetSceneryQuadrant() << 2; + + uint8_t primaryColour = sceneryElement->GetPrimaryColour(); + uint8_t secondaryColour = sceneryElement->GetSecondaryColour(); + + track_design_save_push_tile_element(loc, reinterpret_cast(sceneryElement)); + track_design_save_push_tile_element_desc( + obj, { loc.x, loc.y, sceneryElement->GetBaseZ() }, flags, primaryColour, secondaryColour); + return TrackDesignAddStatus::Success(); } - int32_t entryType = tileElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::LargeScenery, entryType); - sceneryTiles = get_large_scenery_entry(entryType)->tiles; + return TrackDesignAddStatus::Fail(STR_UNSUPPORTED_OBJECT_FORMAT); +} - int32_t z = tileElement->base_height; - direction = tileElement->GetDirection(); - sequence = tileElement->GetSequenceIndex(); - - auto sceneryOrigin = map_large_scenery_get_origin( - { loc.x, loc.y, z << 3, static_cast(direction) }, sequence, nullptr); - if (!sceneryOrigin.has_value()) +static TrackDesignAddStatus track_design_save_add_large_scenery(const CoordsXY& loc, LargeSceneryElement* tileElement) +{ + auto entryIndex = tileElement->GetEntryIndex(); + auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); + auto obj = objectMgr.GetLoadedObject(ObjectType::LargeScenery, entryIndex); + if (obj != nullptr && track_design_is_supported_object(obj)) { - return; - } + auto sceneryEntry = reinterpret_cast(obj->GetLegacyData()); + auto sceneryTiles = sceneryEntry->tiles; - // Iterate through each tile of the large scenery element - sequence = 0; - for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) - { - CoordsXY offsetPos{ tile->x_offset, tile->y_offset }; - auto rotatedOffsetPos = offsetPos.Rotate(direction); + int32_t z = tileElement->base_height; + auto direction = tileElement->GetDirection(); + auto sequence = tileElement->GetSequenceIndex(); - CoordsXYZ tileLoc = { sceneryOrigin->x + rotatedOffsetPos.x, sceneryOrigin->y + rotatedOffsetPos.y, - sceneryOrigin->z + tile->z_offset }; - auto largeElement = map_get_large_scenery_segment({ tileLoc, static_cast(direction) }, sequence); - if (largeElement != nullptr) + auto sceneryOrigin = map_large_scenery_get_origin( + { loc.x, loc.y, z << 3, static_cast(direction) }, sequence, nullptr); + if (!sceneryOrigin.has_value()) { - if (sequence == 0) - { - uint8_t flags = largeElement->GetDirection(); - uint8_t primaryColour = largeElement->GetPrimaryColour(); - uint8_t secondaryColour = largeElement->GetSecondaryColour(); + return TrackDesignAddStatus::Success(); + } - track_design_save_push_tile_element_desc( - entry->GetObjectEntry(), tileLoc, flags, primaryColour, secondaryColour); + // Iterate through each tile of the large scenery element + sequence = 0; + for (auto tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) + { + CoordsXY offsetPos{ tile->x_offset, tile->y_offset }; + auto rotatedOffsetPos = offsetPos.Rotate(direction); + + CoordsXYZ tileLoc = { sceneryOrigin->x + rotatedOffsetPos.x, sceneryOrigin->y + rotatedOffsetPos.y, + sceneryOrigin->z + tile->z_offset }; + auto largeElement = map_get_large_scenery_segment({ tileLoc, static_cast(direction) }, sequence); + if (largeElement != nullptr) + { + if (sequence == 0) + { + uint8_t flags = largeElement->GetDirection(); + uint8_t primaryColour = largeElement->GetPrimaryColour(); + uint8_t secondaryColour = largeElement->GetSecondaryColour(); + + track_design_save_push_tile_element_desc(obj, tileLoc, flags, primaryColour, secondaryColour); + } + track_design_save_push_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); } - track_design_save_push_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); + } + return TrackDesignAddStatus::Success(); + } + + return TrackDesignAddStatus::Fail(STR_UNSUPPORTED_OBJECT_FORMAT); +} + +static TrackDesignAddStatus track_design_save_add_wall(const CoordsXY& loc, WallElement* wallElement) +{ + auto entryIndex = wallElement->GetEntryIndex(); + auto obj = object_entry_get_object(ObjectType::Walls, entryIndex); + if (obj != nullptr && track_design_is_supported_object(obj)) + { + uint8_t flags = 0; + flags |= wallElement->GetDirection(); + flags |= wallElement->GetTertiaryColour() << 2; + + uint8_t secondaryColour = wallElement->GetSecondaryColour(); + uint8_t primaryColour = wallElement->GetPrimaryColour(); + + track_design_save_push_tile_element(loc, reinterpret_cast(wallElement)); + track_design_save_push_tile_element_desc( + obj, { loc.x, loc.y, wallElement->GetBaseZ() }, flags, primaryColour, secondaryColour); + return TrackDesignAddStatus::Success(); + } + + return TrackDesignAddStatus::Fail(STR_UNSUPPORTED_OBJECT_FORMAT); +} + +static std::optional track_design_save_footpath_get_best_entry(PathElement* pathElement) +{ + rct_object_entry pathEntry; + auto legacyPathObj = pathElement->GetLegacyPathEntry(); + if (legacyPathObj != nullptr) + { + pathEntry = legacyPathObj->GetObjectEntry(); + if (!pathEntry.IsEmpty()) + { + return pathEntry; } } + else + { + auto surfaceEntry = pathElement->GetSurfaceEntry(); + if (surfaceEntry != nullptr) + { + auto surfaceId = surfaceEntry->GetIdentifier(); + auto railingsEntry = pathElement->GetRailingsEntry(); + auto railingsId = railingsEntry == nullptr ? "" : railingsEntry->GetIdentifier(); + return GetBestObjectEntryForSurface(surfaceId, railingsId); + } + } + return {}; } -static void track_design_save_add_wall(const CoordsXY& loc, WallElement* wallElement) +static TrackDesignAddStatus track_design_save_add_footpath(const CoordsXY& loc, PathElement* pathElement) { - int32_t entryType = wallElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::Walls, entryType); - - uint8_t flags = 0; - flags |= wallElement->GetDirection(); - flags |= wallElement->GetTertiaryColour() << 2; - - uint8_t secondaryColour = wallElement->GetSecondaryColour(); - uint8_t primaryColour = wallElement->GetPrimaryColour(); - - track_design_save_push_tile_element(loc, reinterpret_cast(wallElement)); - track_design_save_push_tile_element_desc( - entry->GetObjectEntry(), { loc.x, loc.y, wallElement->GetBaseZ() }, flags, primaryColour, secondaryColour); -} - -static void track_design_save_add_footpath(const CoordsXY& loc, PathElement* pathElement) -{ - int32_t entryType = pathElement->GetLegacyPathEntryIndex(); - auto entry = object_entry_get_object(ObjectType::Paths, entryType); + auto pathEntry = track_design_save_footpath_get_best_entry(pathElement); + if (!pathElement) + { + return TrackDesignAddStatus::Fail(STR_UNSUPPORTED_OBJECT_FORMAT); + } uint8_t flags = 0; flags |= pathElement->GetEdges(); @@ -308,37 +380,34 @@ static void track_design_save_add_footpath(const CoordsXY& loc, PathElement* pat flags |= 1 << 7; track_design_save_push_tile_element(loc, reinterpret_cast(pathElement)); - track_design_save_push_tile_element_desc(entry->GetObjectEntry(), { loc.x, loc.y, pathElement->GetBaseZ() }, flags, 0, 0); + track_design_save_push_tile_element_desc(*pathEntry, { loc.x, loc.y, pathElement->GetBaseZ() }, flags, 0, 0); + return TrackDesignAddStatus::Success(); } /** * * rct2: 0x006D2B3C */ -static bool track_design_save_add_tile_element( +static TrackDesignAddStatus track_design_save_add_tile_element( ViewportInteractionItem interactionType, const CoordsXY& loc, TileElement* tileElement) { if (!track_design_save_can_add_tile_element(tileElement)) { - return false; + return TrackDesignAddStatus::Fail(STR_SAVE_TRACK_SCENERY_TOO_MANY_ITEMS_SELECTED); } switch (interactionType) { case ViewportInteractionItem::Scenery: - track_design_save_add_scenery(loc, tileElement->AsSmallScenery()); - return true; + return track_design_save_add_scenery(loc, tileElement->AsSmallScenery()); case ViewportInteractionItem::LargeScenery: - track_design_save_add_large_scenery(loc, tileElement->AsLargeScenery()); - return true; + return track_design_save_add_large_scenery(loc, tileElement->AsLargeScenery()); case ViewportInteractionItem::Wall: - track_design_save_add_wall(loc, tileElement->AsWall()); - return true; + return track_design_save_add_wall(loc, tileElement->AsWall()); case ViewportInteractionItem::Footpath: - track_design_save_add_footpath(loc, tileElement->AsPath()); - return true; + return track_design_save_add_footpath(loc, tileElement->AsPath()); default: - return false; + return TrackDesignAddStatus::Fail(STR_UNKNOWN_OBJECT_TYPE); } } @@ -370,7 +439,7 @@ static void track_design_save_pop_tile_element(const CoordsXY& loc, TileElement* * * rct2: 0x006D2FDD */ -static void track_design_save_pop_tile_element_desc(const rct_object_entry& entry, const CoordsXYZ& loc, uint8_t flags) +static void track_design_save_pop_tile_element_desc(const ObjectEntryDescriptor& entry, const CoordsXYZ& loc, uint8_t flags) { size_t removeIndex = SIZE_MAX; auto tileLoc = TileCoordsXYZ(loc); @@ -399,93 +468,101 @@ static void track_design_save_pop_tile_element_desc(const rct_object_entry& entr static void track_design_save_remove_scenery(const CoordsXY& loc, SmallSceneryElement* sceneryElement) { - int32_t entryType = sceneryElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::SmallScenery, entryType); + auto entryIndex = sceneryElement->GetEntryIndex(); + auto obj = object_entry_get_object(ObjectType::SmallScenery, entryIndex); + if (obj != nullptr) + { + uint8_t flags = 0; + flags |= sceneryElement->GetDirection(); + flags |= sceneryElement->GetSceneryQuadrant() << 2; - uint8_t flags = 0; - flags |= sceneryElement->GetDirection(); - flags |= sceneryElement->GetSceneryQuadrant() << 2; - - track_design_save_pop_tile_element(loc, reinterpret_cast(sceneryElement)); - track_design_save_pop_tile_element_desc(entry->GetObjectEntry(), { loc.x, loc.y, sceneryElement->GetBaseZ() }, flags); + track_design_save_pop_tile_element(loc, reinterpret_cast(sceneryElement)); + track_design_save_pop_tile_element_desc(obj->GetDescriptor(), { loc.x, loc.y, sceneryElement->GetBaseZ() }, flags); + } } static void track_design_save_remove_large_scenery(const CoordsXY& loc, LargeSceneryElement* tileElement) { - rct_large_scenery_tile *sceneryTiles, *tile; - int32_t direction, sequence; - if (tileElement == nullptr) { log_warning("Null tile element"); return; } - int32_t entryType = tileElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::LargeScenery, entryType); - sceneryTiles = get_large_scenery_entry(entryType)->tiles; - - int32_t z = tileElement->base_height; - direction = tileElement->GetDirection(); - sequence = tileElement->GetSequenceIndex(); - - auto sceneryOrigin = map_large_scenery_get_origin( - { loc.x, loc.y, z << 3, static_cast(direction) }, sequence, nullptr); - if (!sceneryOrigin) + auto entryIndex = tileElement->GetEntryIndex(); + auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); + auto obj = objectMgr.GetLoadedObject(ObjectType::LargeScenery, entryIndex); + if (obj != nullptr) { - return; - } + auto sceneryEntry = reinterpret_cast(obj->GetLegacyData()); + auto sceneryTiles = sceneryEntry->tiles; - // Iterate through each tile of the large scenery element - sequence = 0; - for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) - { - CoordsXY offsetPos{ tile->x_offset, tile->y_offset }; - auto rotatedOffsetPos = offsetPos.Rotate(direction); + int32_t z = tileElement->base_height; + auto direction = tileElement->GetDirection(); + auto sequence = tileElement->GetSequenceIndex(); - CoordsXYZ tileLoc = { sceneryOrigin->x + rotatedOffsetPos.x, sceneryOrigin->y + rotatedOffsetPos.y, - sceneryOrigin->z + tile->z_offset }; - auto largeElement = map_get_large_scenery_segment({ tileLoc, static_cast(direction) }, sequence); - if (largeElement != nullptr) + auto sceneryOrigin = map_large_scenery_get_origin( + { loc.x, loc.y, z << 3, static_cast(direction) }, sequence, nullptr); + if (!sceneryOrigin) { - if (sequence == 0) + return; + } + + // Iterate through each tile of the large scenery element + sequence = 0; + for (auto tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) + { + CoordsXY offsetPos{ tile->x_offset, tile->y_offset }; + auto rotatedOffsetPos = offsetPos.Rotate(direction); + + CoordsXYZ tileLoc = { sceneryOrigin->x + rotatedOffsetPos.x, sceneryOrigin->y + rotatedOffsetPos.y, + sceneryOrigin->z + tile->z_offset }; + auto largeElement = map_get_large_scenery_segment({ tileLoc, static_cast(direction) }, sequence); + if (largeElement != nullptr) { - uint8_t flags = largeElement->GetDirection(); - track_design_save_pop_tile_element_desc(entry->GetObjectEntry(), tileLoc, flags); + if (sequence == 0) + { + uint8_t flags = largeElement->GetDirection(); + track_design_save_pop_tile_element_desc(obj->GetDescriptor(), tileLoc, flags); + } + track_design_save_pop_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); } - track_design_save_pop_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); } } } static void track_design_save_remove_wall(const CoordsXY& loc, WallElement* wallElement) { - int32_t entryType = wallElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::Walls, entryType); + auto entryIndex = wallElement->GetEntryIndex(); + auto obj = object_entry_get_object(ObjectType::Walls, entryIndex); + if (obj != nullptr) + { + uint8_t flags = 0; + flags |= wallElement->GetDirection(); + flags |= wallElement->GetTertiaryColour() << 2; - uint8_t flags = 0; - flags |= wallElement->GetDirection(); - flags |= wallElement->GetTertiaryColour() << 2; - - track_design_save_pop_tile_element(loc, reinterpret_cast(wallElement)); - track_design_save_pop_tile_element_desc(entry->GetObjectEntry(), { loc.x, loc.y, wallElement->GetBaseZ() }, flags); + track_design_save_pop_tile_element(loc, reinterpret_cast(wallElement)); + track_design_save_pop_tile_element_desc(obj->GetDescriptor(), { loc.x, loc.y, wallElement->GetBaseZ() }, flags); + } } static void track_design_save_remove_footpath(const CoordsXY& loc, PathElement* pathElement) { - int32_t entryType = pathElement->GetLegacyPathEntryIndex(); - auto entry = object_entry_get_object(ObjectType::Paths, entryType); + auto pathEntry = track_design_save_footpath_get_best_entry(pathElement); + if (pathElement) + { + uint8_t flags = 0; + flags |= pathElement->GetEdges(); + flags |= (pathElement->GetSlopeDirection()) << 5; + if (pathElement->IsSloped()) + flags |= 0b00010000; + if (pathElement->IsQueue()) + flags |= 1 << 7; - uint8_t flags = 0; - flags |= pathElement->GetEdges(); - if (pathElement->IsSloped()) - flags |= (1 << 4); - flags |= (pathElement->GetSlopeDirection()) << 5; - if (pathElement->IsQueue()) - flags |= (1 << 7); - - track_design_save_pop_tile_element(loc, reinterpret_cast(pathElement)); - track_design_save_pop_tile_element_desc(entry->GetObjectEntry(), { loc.x, loc.y, pathElement->GetBaseZ() }, flags); + track_design_save_pop_tile_element(loc, reinterpret_cast(pathElement)); + track_design_save_pop_tile_element_desc( + ObjectEntryDescriptor(*pathEntry), { loc.x, loc.y, pathElement->GetBaseZ() }, flags); + } } /**