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:
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user