From 8a3645a0a5ed4d1de5b524c0036e6ab83d0ef4ca Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 22 Apr 2021 21:06:55 +0100 Subject: [PATCH] Add footpath surface fallback for place track design --- .../actions/FootpathPlaceFromTrackAction.cpp | 44 +++++--- .../actions/FootpathPlaceFromTrackAction.h | 7 +- src/openrct2/ride/TrackDesign.cpp | 106 +++++++++++------- src/openrct2/ride/TrackDesign.h | 5 + 4 files changed, 107 insertions(+), 55 deletions(-) diff --git a/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp b/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp index 959eee1c1c..bfe98eb155 100644 --- a/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp +++ b/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp @@ -22,12 +22,14 @@ #include "../world/Wall.h" FootpathPlaceFromTrackAction::FootpathPlaceFromTrackAction( - const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, uint8_t edges, bool isQueue) + const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, ObjectEntryIndex railingsType, uint8_t edges, + PathConstructFlags constructFlags) : _loc(loc) , _slope(slope) , _type(type) + , _railingsType(railingsType) , _edges(edges) - , _isQueue(isQueue) + , _constructFlags(constructFlags) { } @@ -35,7 +37,8 @@ void FootpathPlaceFromTrackAction::Serialise(DataSerialiser& stream) { GameAction::Serialise(stream); - stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_edges) << DS_TAG(_isQueue); + stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_railingsType) << DS_TAG(_edges) + << DS_TAG(_constructFlags); } uint16_t FootpathPlaceFromTrackAction::GetActionFlags() const @@ -130,9 +133,9 @@ GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertQuery(GameAc } // Do not attempt to build a crossing with a queue or a sloped. - uint8_t crossingMode = _isQueue || (_slope != TILE_ELEMENT_SLOPE_FLAT) - ? CREATE_CROSSING_MODE_NONE - : CREATE_CROSSING_MODE_PATH_OVER_TRACK; + auto isQueue = _constructFlags & PathConstructFlag::IsQueue; + uint8_t crossingMode = isQueue || (_slope != TILE_ELEMENT_SLOPE_FLAT) ? CREATE_CROSSING_MODE_NONE + : CREATE_CROSSING_MODE_PATH_OVER_TRACK; if (!entrancePath && !map_can_construct_with_clear_at( { _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), &res->Cost, crossingMode)) @@ -197,9 +200,9 @@ GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertExecute(Game } // Do not attempt to build a crossing with a queue or a sloped. - uint8_t crossingMode = _isQueue || (_slope != TILE_ELEMENT_SLOPE_FLAT) - ? CREATE_CROSSING_MODE_NONE - : CREATE_CROSSING_MODE_PATH_OVER_TRACK; + auto isQueue = _constructFlags & PathConstructFlag::IsQueue; + uint8_t crossingMode = isQueue || (_slope != TILE_ELEMENT_SLOPE_FLAT) ? CREATE_CROSSING_MODE_NONE + : CREATE_CROSSING_MODE_PATH_OVER_TRACK; if (!entrancePath && !map_can_construct_with_clear_at( { _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GAME_COMMAND_FLAG_APPLY | GetFlags(), @@ -224,8 +227,14 @@ GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertExecute(Game { if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !entranceIsSamePath) { - // Set the path type but make sure it's not a queue as that will not show up - entranceElement->SetSurfaceEntryIndex(_type); + if (_constructFlags & PathConstructFlag::IsPathObject) + { + entranceElement->SetPathEntryIndex(_type); + } + else + { + entranceElement->SetSurfaceEntryIndex(_type); + } map_invalidate_tile_full(_loc); } } @@ -235,11 +244,18 @@ GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertExecute(Game Guard::Assert(pathElement != nullptr); pathElement->SetClearanceZ(zHigh); - pathElement->SetSurfaceEntryIndex(_type); - pathElement->SetRailingEntryIndex(OBJECT_ENTRY_INDEX_NULL); + if (_constructFlags & PathConstructFlag::IsPathObject) + { + pathElement->SetPathEntryIndex(_type); + } + else + { + pathElement->SetSurfaceEntryIndex(_type); + pathElement->SetRailingEntryIndex(_railingsType); + } pathElement->SetSlopeDirection(_slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK); pathElement->SetSloped(_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED); - pathElement->SetIsQueue(_isQueue); + pathElement->SetIsQueue(isQueue); pathElement->SetAddition(0); pathElement->SetRideIndex(RIDE_ID_NULL); pathElement->SetAdditionStatus(255); diff --git a/src/openrct2/actions/FootpathPlaceFromTrackAction.h b/src/openrct2/actions/FootpathPlaceFromTrackAction.h index 36d253e22a..273ab728cd 100644 --- a/src/openrct2/actions/FootpathPlaceFromTrackAction.h +++ b/src/openrct2/actions/FootpathPlaceFromTrackAction.h @@ -17,12 +17,15 @@ private: CoordsXYZ _loc; uint8_t _slope{}; ObjectEntryIndex _type{}; + ObjectEntryIndex _railingsType{}; uint8_t _edges{}; - bool _isQueue{}; + PathConstructFlags _constructFlags{}; public: FootpathPlaceFromTrackAction() = default; - FootpathPlaceFromTrackAction(const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, uint8_t edges, bool isQueue); + FootpathPlaceFromTrackAction( + const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, ObjectEntryIndex railingsType, uint8_t edges, + PathConstructFlags constructFlags = 0); uint16_t GetActionFlags() const override; diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 4c470db6fc..dedd8811e3 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -856,61 +856,88 @@ 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) +struct TrackSceneryEntry { + ObjectType Type = ObjectType::None; + ObjectEntryIndex Index = OBJECT_ENTRY_INDEX_NULL; + ObjectEntryIndex SecondaryIndex = OBJECT_ENTRY_INDEX_NULL; // For footpath railing +}; + +static std::optional TrackDesignPlaceSceneryElementGetEntry(const TrackDesignSceneryElement& scenery) +{ + TrackSceneryEntry result; auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); auto obj = objectMgr.GetLoadedObject(scenery.scenery_object); if (obj != nullptr) { - entry_type = obj->GetObjectType(); - entry_index = objectMgr.GetLoadedObjectEntryIndex(obj); + result.Type = obj->GetObjectType(); + result.Index = objectMgr.GetLoadedObjectEntryIndex(obj); } else { - entry_type = scenery.scenery_object.GetType(); - if (entry_type != ObjectType::Paths) + // Find a fallback footpath + result.Type = scenery.scenery_object.GetType(); + if (result.Type != ObjectType::Paths) { + // Unsupported object type _trackDesignPlaceStateSceneryUnavailable = true; - return true; + return {}; } if (gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) { + // Don't bother with fallback with track designer _trackDesignPlaceStateSceneryUnavailable = true; - return true; + return {}; } - entry_index = 0; - for (const auto* path = get_path_surface_entry(0); - entry_index < object_entry_group_counts[EnumValue(ObjectType::FootpathSurface)]; - path = get_path_surface_entry(entry_index), entry_index++) + // Find a default footpath surface to use + auto isQueue = scenery.IsQueue(); + for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_SURFACE_OBJECTS; i++) { - if (path == nullptr) + auto footpathSurfaceObj = get_path_surface_entry(i); + if (footpathSurfaceObj != nullptr) { - return true; - } - if (path->Flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) - { - return true; + if (footpathSurfaceObj->Flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) + { + continue; + } + + if (isQueue != ((footpathSurfaceObj->Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0)) + { + continue; + } + + result.Index = i; + break; } } - if (entry_index == object_entry_group_counts[EnumValue(ObjectType::Paths)]) + // Find a default railing to use + for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_RAILINGS_OBJECTS; i++) + { + auto footpathRailingsObj = get_path_railings_entry(i); + if (footpathRailingsObj != nullptr) + { + result.SecondaryIndex = i; + break; + } + } + + if (result.Index == OBJECT_ENTRY_INDEX_NULL || result.SecondaryIndex == OBJECT_ENTRY_INDEX_NULL) { _trackDesignPlaceStateSceneryUnavailable = true; - return true; + return {}; } } - return false; + return result; } 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; } @@ -925,14 +952,14 @@ static bool TrackDesignPlaceSceneryElementRemoveGhost( const uint32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; std::unique_ptr ga; - switch (entry_type) + switch (entryInfo->Type) { case ObjectType::SmallScenery: { uint8_t quadrant = (scenery.flags >> 2) + _currentTrackPieceDirection; quadrant &= 3; - rct_scenery_entry* small_scenery = get_small_scenery_entry(entry_index); + rct_scenery_entry* small_scenery = get_small_scenery_entry(entryInfo->Index); if (!(!scenery_small_entry_has_flag(small_scenery, SMALL_SCENERY_FLAG_FULL_TILE) && scenery_small_entry_has_flag(small_scenery, SMALL_SCENERY_FLAG_DIAGONAL)) && scenery_small_entry_has_flag( @@ -942,7 +969,7 @@ static bool TrackDesignPlaceSceneryElementRemoveGhost( quadrant = 0; } - ga = std::make_unique(CoordsXYZ{ mapCoord.x, mapCoord.y, z }, quadrant, entry_index); + ga = std::make_unique(CoordsXYZ{ mapCoord.x, mapCoord.y, z }, quadrant, entryInfo->Index); break; } case ObjectType::LargeScenery: @@ -970,10 +997,7 @@ static bool TrackDesignPlaceSceneryElementGetPlaceZ(const TrackDesignSceneryElem _trackDesignPlaceSceneryZ = z; } - ObjectType entry_type; - ObjectEntryIndex entry_index; - TrackDesignPlaceSceneryElementGetEntry(entry_type, entry_index, scenery); - + TrackDesignPlaceSceneryElementGetEntry(scenery); return true; } @@ -1000,9 +1024,8 @@ static bool 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 true; } @@ -1012,7 +1035,7 @@ static bool TrackDesignPlaceSceneryElement( uint8_t flags; uint8_t quadrant; - switch (entry_type) + switch (entryInfo->Type) { case ObjectType::SmallScenery: { @@ -1052,7 +1075,7 @@ static bool 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); @@ -1098,7 +1121,8 @@ static bool 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); @@ -1141,7 +1165,7 @@ static bool 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) @@ -1188,8 +1212,12 @@ static bool TrackDesignPlaceSceneryElement( } uint8_t slope = ((bh >> 5) & 0x3) | ((bh >> 2) & 0x4); uint8_t edges = bh & 0xF; + PathConstructFlags constructFlags = 0; + if (isQueue) + constructFlags |= PathConstructFlag::IsQueue; auto footpathPlaceAction = FootpathPlaceFromTrackAction( - { mapCoord.x, mapCoord.y, z * COORDS_Z_STEP }, slope, entry_index, edges, isQueue); + { 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); diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index 6fb3432d99..6fefb70e65 100644 --- a/src/openrct2/ride/TrackDesign.h +++ b/src/openrct2/ride/TrackDesign.h @@ -39,6 +39,11 @@ struct TrackDesignSceneryElement uint8_t flags; uint8_t primary_colour; uint8_t secondary_colour; + + bool IsQueue() const + { + return (flags & (1 << 7)) != 0; + } }; /**