mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-20 05:23:04 +01:00
Add footpath surface fallback for place track design
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<TrackSceneryEntry> 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<GameAction> 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<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:
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user