mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-24 23:34:37 +01:00
Backport track design changes
Co-authored-by: Ted John <ted@brambles.org>
This commit is contained in:
@@ -598,6 +598,50 @@ template<> struct DataSerializerTraits_t<rct_object_entry>
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits_t<ObjectEntryDescriptor>
|
||||
{
|
||||
static void encode(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val)
|
||||
{
|
||||
stream->WriteValue<uint8_t>(static_cast<uint8_t>(val.Generation));
|
||||
if (val.Generation == ObjectGeneration::DAT)
|
||||
{
|
||||
DataSerializerTraits<rct_object_entry> s;
|
||||
s.encode(stream, val.Entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->WriteValue<uint8_t>(static_cast<uint8_t>(val.GetType()));
|
||||
stream->WriteString(val.Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
static void decode(OpenRCT2::IStream* stream, ObjectEntryDescriptor& val)
|
||||
{
|
||||
auto generation = static_cast<ObjectGeneration>(stream->ReadValue<uint8_t>());
|
||||
if (generation == ObjectGeneration::DAT)
|
||||
{
|
||||
DataSerializerTraits<rct_object_entry> s;
|
||||
rct_object_entry entry;
|
||||
s.decode(stream, entry);
|
||||
val = ObjectEntryDescriptor(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto type = static_cast<ObjectType>(stream->ReadValue<uint8_t>());
|
||||
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<TrackDesignTrackElement>
|
||||
{
|
||||
static void encode(OpenRCT2::IStream* stream, const TrackDesignTrackElement& val)
|
||||
@@ -677,7 +721,7 @@ template<> struct DataSerializerTraits_t<TrackDesignSceneryElement>
|
||||
stream->Write(&val.flags);
|
||||
stream->Write(&val.primary_colour);
|
||||
stream->Write(&val.secondary_colour);
|
||||
DataSerializerTraits<rct_object_entry> s;
|
||||
DataSerializerTraits<ObjectEntryDescriptor> s;
|
||||
s.encode(stream, val.scenery_object);
|
||||
}
|
||||
static void decode(OpenRCT2::IStream* stream, TrackDesignSceneryElement& val)
|
||||
@@ -688,7 +732,7 @@ template<> struct DataSerializerTraits_t<TrackDesignSceneryElement>
|
||||
stream->Read(&val.flags);
|
||||
stream->Read(&val.primary_colour);
|
||||
stream->Read(&val.secondary_colour);
|
||||
DataSerializerTraits<rct_object_entry> s;
|
||||
DataSerializerTraits<ObjectEntryDescriptor> s;
|
||||
s.decode(stream, val.scenery_object);
|
||||
}
|
||||
static void log(OpenRCT2::IStream* stream, const TrackDesignSceneryElement& val)
|
||||
@@ -698,7 +742,9 @@ template<> struct DataSerializerTraits_t<TrackDesignSceneryElement>
|
||||
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<rct_vehicle_colour>
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits_t<ObjectEntryDescriptor>
|
||||
{
|
||||
static void encode(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val)
|
||||
{
|
||||
stream->Write(&val.Generation);
|
||||
if (val.Generation == ObjectGeneration::DAT)
|
||||
{
|
||||
DataSerializerTraits<rct_object_entry> s;
|
||||
s.encode(stream, val.Entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
DataSerializerTraits<std::string> 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<rct_object_entry> s;
|
||||
s.decode(stream, obj);
|
||||
val = ObjectEntryDescriptor(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string id;
|
||||
DataSerializerTraits<std::string> 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<int32_t>(val.Generation));
|
||||
stream->Write(msg, strlen(msg));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits_t<IntensityRange>
|
||||
{
|
||||
static void encode(OpenRCT2::IStream* stream, const IntensityRange& val)
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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<uint8_t> GetStyleFromMusicIdentifier(std::string_view identifier)
|
||||
{
|
||||
auto it = std::find(std::begin(_musicStyles), std::end(_musicStyles), identifier);
|
||||
@@ -1408,3 +1408,19 @@ std::optional<uint8_t> 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);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
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<uint8_t> GetStyleFromMusicIdentifier(std::string_view identifier);
|
||||
|
||||
static constexpr money32 RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE = 0x80000001;
|
||||
|
||||
@@ -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<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;
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
@@ -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<rct_object_entry> GetBestObjectEntryForSurface(std::string_view surface, std::string_view railings);
|
||||
|
||||
@@ -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<uint32_t>(_trackDesign->flags2);
|
||||
tempStream.Write(&_trackDesign->vehicle_object, sizeof(rct_object_entry));
|
||||
tempStream.Write(&_trackDesign->vehicle_object.Entry, sizeof(rct_object_entry));
|
||||
tempStream.WriteValue<uint8_t>(_trackDesign->space_required_x);
|
||||
tempStream.WriteValue<uint8_t>(_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<int8_t>(sceneryElement.x);
|
||||
tempStream.WriteValue<int8_t>(sceneryElement.y);
|
||||
tempStream.WriteValue<int8_t>(sceneryElement.z);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<TrackSceneryEntry> 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<LargeSceneryEntry*>(object_entry_get_chunk(entry_type, entryIndex));
|
||||
auto* sceneryEntry = reinterpret_cast<const LargeSceneryEntry*>(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<SmallSceneryEntry*>(object_entry_get_chunk(entry_type, entryIndex));
|
||||
auto* sceneryEntry = reinterpret_cast<const SmallSceneryEntry*>(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<GameAction> 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<SmallSceneryRemoveAction>(CoordsXYZ{ mapCoord.x, mapCoord.y, z }, quadrant, entry_index);
|
||||
ga = std::make_unique<SmallSceneryRemoveAction>(CoordsXYZ{ mapCoord.x, mapCoord.y, z }, quadrant, entryInfo->Index);
|
||||
break;
|
||||
}
|
||||
case ObjectType::LargeScenery:
|
||||
@@ -952,6 +1006,7 @@ static bool TrackDesignPlaceSceneryElementRemoveGhost(
|
||||
ga = std::make_unique<WallRemoveAction>(CoordsXYZD{ mapCoord.x, mapCoord.y, z, sceneryRotation });
|
||||
break;
|
||||
case ObjectType::Paths:
|
||||
case ObjectType::FootpathSurface:
|
||||
ga = std::make_unique<FootpathRemoveAction>(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<money32> 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<money32> TrackDesignPlaceSceneryElement(
|
||||
uint8_t flags;
|
||||
uint8_t quadrant;
|
||||
|
||||
switch (entry_type)
|
||||
switch (entryInfo->Type)
|
||||
{
|
||||
case ObjectType::SmallScenery:
|
||||
{
|
||||
@@ -1059,7 +1110,7 @@ static std::optional<money32> 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<money32> 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<money32> 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<money32> TrackDesignPlaceSceneryElement(
|
||||
break;
|
||||
}
|
||||
case ObjectType::Paths:
|
||||
case ObjectType::FootpathSurface:
|
||||
if (_trackDesignPlaceOperation == PTD_OPERATION_GET_PLACE_Z)
|
||||
{
|
||||
return 0;
|
||||
@@ -1196,12 +1249,14 @@ static std::optional<money32> 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++)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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<const TileElement*> _trackSavedTileElements;
|
||||
std::vector<TrackDesignSceneryElement> _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<TileElement*>(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<TileElement*>(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>(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<const LargeSceneryEntry*>(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>(direction) }, sequence);
|
||||
if (largeElement != nullptr)
|
||||
auto sceneryOrigin = map_large_scenery_get_origin(
|
||||
{ loc.x, loc.y, z << 3, static_cast<Direction>(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>(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<TileElement*>(largeElement));
|
||||
}
|
||||
track_design_save_push_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast<TileElement*>(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<TileElement*>(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<rct_object_entry> 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<TileElement*>(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<TileElement*>(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<TileElement*>(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<TileElement*>(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>(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<const LargeSceneryEntry*>(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>(direction) }, sequence);
|
||||
if (largeElement != nullptr)
|
||||
auto sceneryOrigin = map_large_scenery_get_origin(
|
||||
{ loc.x, loc.y, z << 3, static_cast<Direction>(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>(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<TileElement*>(largeElement));
|
||||
}
|
||||
track_design_save_pop_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast<TileElement*>(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<TileElement*>(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<TileElement*>(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<TileElement*>(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<TileElement*>(pathElement));
|
||||
track_design_save_pop_tile_element_desc(
|
||||
ObjectEntryDescriptor(*pathEntry), { loc.x, loc.y, pathElement->GetBaseZ() }, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user