From 69ab87860c81333f64afc9bb72097cef76565d08 Mon Sep 17 00:00:00 2001 From: Ted John Date: Fri, 23 Apr 2021 00:57:22 +0100 Subject: [PATCH] Fix saving of track designs with footpaths --- src/openrct2/rct2/RCT2.cpp | 97 +++++++++++++++++++++------ src/openrct2/rct2/RCT2.h | 11 ++- src/openrct2/rct2/S6Importer.cpp | 16 ++--- src/openrct2/ride/TrackDesign.cpp | 11 +-- src/openrct2/ride/TrackDesignSave.cpp | 79 +++++++++++++++------- 5 files changed, 156 insertions(+), 58 deletions(-) diff --git a/src/openrct2/rct2/RCT2.cpp b/src/openrct2/rct2/RCT2.cpp index 0b62a48be8..698903d599 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" @@ -162,25 +164,82 @@ RCT12TrackType OpenRCT2TrackTypeToRCT2(track_type_t origTrackType) return OpenRCT2FlatTrackTypeToRCT12(origTrackType); } -std::tuple GetFootpathSurfaceId(const ObjectEntryDescriptor& desc) +static FootpathMapping _footpathMappings[] = { + // RCT2 mappings + { "PATHASH ", "rct2.pathsurface.ash", "rct2.pathsurface.queue.yellow", "rct2.railings.bambooblack" }, + { "PATHCRZY", "rct2.pathsurface.crazy", "rct2.pathsurface.queue.yellow", "rct2.railings.concrete" }, + { "PATHDIRT", "rct2.pathsurface.dirt", "rct2.pathsurface.queue.yellow", "rct2.railings.bamboobrown" }, + { "PATHSPCE", "rct2.pathsurface.space", "rct2.pathsurface.queue.red", "rct2.railings.space" }, + { "ROAD ", "rct2.pathsurface.road", "rct2.pathsurface.queue.blue", "rct2.railings.wood" }, + { "TARMACB ", "rct2.pathsurface.tarmac.brown", "rct2.pathsurface.queue.yellow", "rct2.railings.concrete" }, + { "TARMACG ", "rct2.pathsurface.tarmac.green", "rct2.pathsurface.queue.green", "rct2.railings.concretegreen" }, + { "TARMAC ", "rct2.pathsurface.tarmac", "rct2.pathsurface.queue.blue", "rct2.railings.wood" }, + + // RCT 1 mappings (for reverse lookup) + { "PATHASH ", "rct1.aa.pathsurface.ash", "rct1.aa.pathsurface.queue.yellow", "rct2.railings.bambooblack" }, + { "PATHCRZY", "rct1.pathsurface.crazy", "rct1.aa.pathsurface.queue.yellow", "rct2.railings.concrete" }, + { "PATHDIRT", "rct1.pathsurface.dirt", "rct1.aa.pathsurface.queue.yellow", "rct2.railings.bamboobrown" }, + { "PATHSPCE", "rct1.aa.pathsurface.space", "rct1.pathsurface.queue.red", "rct1.ll.railings.space" }, + { "TARMACB ", "rct1.aa.pathsurface.tarmac.brown", "rct1.aa.pathsurface.queue.yellow", "rct2.railings.concrete" }, + { "TARMACG ", "rct1.aa.pathsurface.tarmac.green", "rct1.aa.pathsurface.queue.green", "rct2.railings.concretegreen" }, + { "TARMAC ", "rct1.pathsurface.tarmac", "rct1.pathsurface.queue.blue", "rct2.railings.wood" }, + { "PATHCRZY", "rct1.pathsurface.tile.brown", "rct1.aa.pathsurface.queue.yellow", "rct2.railings.concrete" }, + { "PATHCRZY", "rct1.aa.pathsurface.tile.grey", "rct1.pathsurface.queue.blue", "rct2.railings.concrete" }, + { "PATHCRZY", "rct1.ll.pathsurface.tile.red", "rct1.pathsurface.queue.red", "rct2.railings.concrete" }, + { "PATHCRZY", "rct1.ll.pathsurface.tile.green", "rct1.aa.pathsurface.queue.green", "rct2.railings.concrete" }, +}; + +const FootpathMapping* GetFootpathSurfaceId(const ObjectEntryDescriptor& desc, bool ideallyLoaded, bool isQueue) { + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto name = desc.Entry.GetName(); - if (name == "PATHASH ") - return { "rct2.pathsurface.ash", "rct2.pathsurface.queue.yellow", "rct2.railings.bambooblack" }; - else if (name == "PATHCRZY") - return { "rct2.pathsurface.crazy", "rct2.pathsurface.queue.yellow", "rct2.railings.concrete" }; - else if (name == "PATHDIRT") - return { "rct2.pathsurface.dirt", "rct2.pathsurface.queue.yellow", "rct2.railings.bamboobrown" }; - else if (name == "PATHSPCE") - return { "rct2.pathsurface.space", "rct2.pathsurface.queue.red", "rct2.railings.space" }; - else if (name == "ROAD ") - return { "rct2.pathsurface.road", "rct2.pathsurface.queue.blue", "rct2.railings.wood" }; - else if (name == "TARMACB ") - return { "rct2.pathsurface.tarmac.brown", "rct2.pathsurface.queue.yellow", "rct2.railings.concrete" }; - else if (name == "TARMACG ") - return { "rct2.pathsurface.tarmac.green", "rct2.pathsurface.queue.green", "rct2.railings.concretegreen" }; - else if (name == "TARMAC ") - return { "rct2.pathsurface.tarmac", "rct2.pathsurface.queue.blue", "rct2.railings.wood" }; - else - return {}; + 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; + } + else 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 369e6a9782..e99957989c 100644 --- a/src/openrct2/rct2/RCT2.h +++ b/src/openrct2/rct2/RCT2.h @@ -804,4 +804,13 @@ RCT12TrackType OpenRCT2TrackTypeToRCT2(track_type_t origTrackType); */ size_t GetRCT2StringBufferLen(const char* buffer, size_t maxBufferLen); -std::tuple GetFootpathSurfaceId(const ObjectEntryDescriptor& desc); +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/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 272b524e98..c5019673ee 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -1604,8 +1604,8 @@ public: { if (objectType == EnumValue(ObjectType::Paths)) { - auto [normal, queue, railings] = GetFootpathSurfaceId(entry); - if (normal.empty() && queue.empty() && railings.empty()) + auto footpathMapping = GetFootpathSurfaceId(entry); + if (footpathMapping == nullptr) { // Unsupported footpath objectList.SetObject(i, entry); @@ -1613,26 +1613,26 @@ public: else { // We have surface objects for this footpath - auto surfaceIndex = objectList.Find(ObjectType::FootpathSurface, normal); + auto surfaceIndex = objectList.Find(ObjectType::FootpathSurface, footpathMapping->NormalSurface); if (surfaceIndex == OBJECT_ENTRY_INDEX_NULL) { - objectList.SetObject(ObjectType::FootpathSurface, surfaceCount, normal); + objectList.SetObject(ObjectType::FootpathSurface, surfaceCount, footpathMapping->NormalSurface); surfaceIndex = surfaceCount++; } _pathToSurfaceMap[i] = surfaceIndex; - surfaceIndex = objectList.Find(ObjectType::FootpathSurface, queue); + surfaceIndex = objectList.Find(ObjectType::FootpathSurface, footpathMapping->QueueSurface); if (surfaceIndex == OBJECT_ENTRY_INDEX_NULL) { - objectList.SetObject(ObjectType::FootpathSurface, surfaceCount, queue); + objectList.SetObject(ObjectType::FootpathSurface, surfaceCount, footpathMapping->QueueSurface); surfaceIndex = surfaceCount++; } _pathToQueueSurfaceMap[i] = surfaceIndex; - auto railingIndex = objectList.Find(ObjectType::FootpathRailings, railings); + auto railingIndex = objectList.Find(ObjectType::FootpathRailings, footpathMapping->Railing); if (railingIndex == OBJECT_ENTRY_INDEX_NULL) { - objectList.SetObject(ObjectType::FootpathRailings, railingCount, railings); + objectList.SetObject(ObjectType::FootpathRailings, railingCount, footpathMapping->Railing); railingIndex = railingCount++; } _pathToRailingMap[i] = railingIndex; diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 3a6fd3f1ea..93910e8a3f 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -704,8 +704,8 @@ static std::optional TrackDesignPlaceSceneryElementGetEntry(c auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); if (scenery.scenery_object.GetType() == ObjectType::Paths) { - auto [normal, queue, railings] = GetFootpathSurfaceId(scenery.scenery_object); - if (normal.empty() && queue.empty() && railings.empty()) + 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); @@ -722,12 +722,13 @@ static std::optional TrackDesignPlaceSceneryElementGetEntry(c else { result.Type = ObjectType::FootpathSurface; - result.Index = objectMgr.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(scenery.IsQueue() ? queue : normal)); - result.SecondaryIndex = objectMgr.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(railings)); + 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(false); + result.Index = TrackDesignGetDefaultSurfaceIndex(scenery.IsQueue()); if (result.SecondaryIndex == OBJECT_ENTRY_INDEX_NULL) result.SecondaryIndex = TrackDesignGetDefaultRailingIndex(); diff --git a/src/openrct2/ride/TrackDesignSave.cpp b/src/openrct2/ride/TrackDesignSave.cpp index 7d38310c0d..b955ada397 100644 --- a/src/openrct2/ride/TrackDesignSave.cpp +++ b/src/openrct2/ride/TrackDesignSave.cpp @@ -14,6 +14,9 @@ #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" @@ -196,20 +199,9 @@ static bool track_design_is_supported_object(const Object* obj) return !entry.IsEmpty(); } -/** - * - * rct2: 0x006D2FA7 - */ static void track_design_save_push_tile_element_desc( - const Object* obj, const CoordsXYZ& loc, uint8_t flags, uint8_t primaryColour, uint8_t secondaryColour) + const rct_object_entry& entry, const CoordsXYZ& loc, uint8_t flags, uint8_t primaryColour, uint8_t secondaryColour) { - const auto& entry = obj->GetObjectEntry(); - if (entry.IsEmpty()) - { - // Unsupported, should have been blocked earlier - assert(false); - } - auto tileLoc = TileCoordsXYZ(loc); TrackDesignSceneryElement item{}; item.scenery_object = ObjectEntryDescriptor(entry); @@ -223,6 +215,19 @@ static void track_design_save_push_tile_element_desc( _trackSavedTileElementsDesc.push_back(std::move(item)); } +static void track_design_save_push_tile_element_desc( + const Object* obj, const CoordsXYZ& loc, uint8_t flags, uint8_t primaryColour, uint8_t secondaryColour) +{ + const auto& entry = obj->GetObjectEntry(); + if (entry.IsEmpty()) + { + // Unsupported, should have been blocked earlier + assert(false); + } + + track_design_save_push_tile_element_desc(entry, loc, flags, primaryColour, secondaryColour); +} + static void track_design_save_add_scenery(const CoordsXY& loc, SmallSceneryElement* sceneryElement) { auto entryIndex = sceneryElement->GetEntryIndex(); @@ -316,21 +321,45 @@ static void track_design_save_add_wall(const CoordsXY& loc, WallElement* wallEle static void track_design_save_add_footpath(const CoordsXY& loc, PathElement* pathElement) { - auto entryIndex = pathElement->GetSurfaceEntryIndex(); - auto obj = object_entry_get_object(ObjectType::Paths, entryIndex); - if (obj != nullptr && track_design_is_supported_object(obj)) + rct_object_entry pathEntry; + auto legacyPathObj = pathElement->GetPathEntry(); + if (legacyPathObj != nullptr) { - uint8_t flags = 0; - flags |= pathElement->GetEdges(); - flags |= (pathElement->GetSlopeDirection()) << 5; - if (pathElement->IsSloped()) - flags |= 0b00010000; - if (pathElement->IsQueue()) - flags |= 1 << 7; - - track_design_save_push_tile_element(loc, reinterpret_cast(pathElement)); - track_design_save_push_tile_element_desc(obj, { loc.x, loc.y, pathElement->GetBaseZ() }, flags, 0, 0); + pathEntry = legacyPathObj->GetObjectEntry(); + if (pathEntry.IsEmpty()) + { + return; + } } + else + { + auto surfaceEntry = pathElement->GetSurfaceEntry(); + if (surfaceEntry == nullptr) + { + return; + } + + auto surfaceId = surfaceEntry->GetIdentifier(); + auto railingsEntry = pathElement->GetRailingEntry(); + auto railingsId = railingsEntry == nullptr ? "" : railingsEntry->GetIdentifier(); + + auto bestPathEntry = GetBestObjectEntryForSurface(surfaceId, railingsId); + if (!bestPathEntry) + return; + + pathEntry = *bestPathEntry; + } + + uint8_t flags = 0; + flags |= pathElement->GetEdges(); + flags |= (pathElement->GetSlopeDirection()) << 5; + if (pathElement->IsSloped()) + flags |= 0b00010000; + if (pathElement->IsQueue()) + flags |= 1 << 7; + + track_design_save_push_tile_element(loc, reinterpret_cast(pathElement)); + track_design_save_push_tile_element_desc(pathEntry, { loc.x, loc.y, pathElement->GetBaseZ() }, flags, 0, 0); } /**