1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-17 20:13:07 +01:00

Fix saving of track designs with footpaths

This commit is contained in:
Ted John
2021-04-23 00:57:22 +01:00
parent 5cba4fe1a7
commit 69ab87860c
5 changed files with 156 additions and 58 deletions

View File

@@ -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<std::string_view, std::string_view, std::string_view> 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<rct_object_entry> 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 {};
}

View File

@@ -804,4 +804,13 @@ RCT12TrackType OpenRCT2TrackTypeToRCT2(track_type_t origTrackType);
*/
size_t GetRCT2StringBufferLen(const char* buffer, size_t maxBufferLen);
std::tuple<std::string_view, std::string_view, std::string_view> 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<rct_object_entry> GetBestObjectEntryForSurface(std::string_view surface, std::string_view railings);

View File

@@ -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;

View File

@@ -704,8 +704,8 @@ static std::optional<TrackSceneryEntry> 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<TrackSceneryEntry> 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();

View File

@@ -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<TileElement*>(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<TileElement*>(pathElement));
track_design_save_push_tile_element_desc(pathEntry, { loc.x, loc.y, pathElement->GetBaseZ() }, flags, 0, 0);
}
/**