1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-10 17:42:29 +01:00

Introduce TileElementsView (#13975)

* Simplify TileElement type conversation

* Introduce TileElementsView

* Move TileElementsView code into TileElementsView.h

* Cleanup code and move into OpenRCT2 namespace

* Use reference instead of pointer

* Fix include

* Make GCC happy

* Move the cast functions into base

* Use the cast function instead of reinterpret_cast

* Add TileElementsView tests

* Fix iterating on TileElementBase, return pointer not reference
This commit is contained in:
ζeh Matt
2021-02-04 18:58:45 +02:00
committed by GitHub
parent b64d7d7dd7
commit 6186766a05
6 changed files with 418 additions and 120 deletions

View File

@@ -453,6 +453,7 @@
<ClInclude Include="world\SpriteBase.h" />
<ClInclude Include="world\Surface.h" />
<ClInclude Include="world\TileElement.h" />
<ClInclude Include="world\TileElementsView.h" />
<ClInclude Include="world\TileInspector.h" />
<ClInclude Include="world\Wall.h" />
<ClInclude Include="world\Water.h" />

View File

@@ -52,6 +52,7 @@
#include "Scenery.h"
#include "SmallScenery.h"
#include "Surface.h"
#include "TileElementsView.h"
#include "TileInspector.h"
#include "Wall.h"
@@ -215,67 +216,35 @@ void map_set_tile_element(const TileCoordsXY& tilePos, TileElement* elements)
SurfaceElement* map_get_surface_element_at(const CoordsXY& coords)
{
TileElement* tileElement = map_get_first_element_at(coords);
auto view = TileElementsView<SurfaceElement>(coords);
if (tileElement == nullptr)
return nullptr;
// Find the first surface element
while (tileElement->GetType() != TILE_ELEMENT_TYPE_SURFACE)
{
if (tileElement->IsLastForTile())
return nullptr;
tileElement++;
}
return tileElement->AsSurface();
return *view.begin();
}
PathElement* map_get_path_element_at(const TileCoordsXYZ& loc)
{
TileElement* tileElement = map_get_first_element_at(loc.ToCoordsXY());
if (tileElement == nullptr)
return nullptr;
// Find the path element at known z
do
for (auto* element : TileElementsView<PathElement>(loc.ToCoordsXY()))
{
if (tileElement->IsGhost())
if (element->IsGhost())
continue;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH)
if (element->base_height != loc.z)
continue;
if (tileElement->base_height != loc.z)
continue;
return tileElement->AsPath();
} while (!(tileElement++)->IsLastForTile());
return element;
}
return nullptr;
}
BannerElement* map_get_banner_element_at(const CoordsXYZ& bannerPos, uint8_t position)
{
auto bannerTilePos = TileCoordsXYZ{ bannerPos };
TileElement* tileElement = map_get_first_element_at(bannerPos);
if (tileElement == nullptr)
return nullptr;
// Find the banner element at known z and position
do
const auto bannerTilePos = TileCoordsXYZ{ bannerPos };
for (auto* element : TileElementsView<BannerElement>(bannerPos))
{
if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER)
if (element->base_height != bannerTilePos.z)
continue;
if (tileElement->base_height != bannerTilePos.z)
if (element->GetPosition() != position)
continue;
if (tileElement->AsBanner()->GetPosition() != position)
continue;
return tileElement->AsBanner();
} while (!(tileElement++)->IsLastForTile());
return element;
}
return nullptr;
}

View File

@@ -99,6 +99,80 @@ struct TileElementBase
uint8_t GetOwner() const;
void SetOwner(uint8_t newOwner);
template<typename TType> const TType* as() const
{
return static_cast<TileElementType>(GetType()) == TType::ElementType ? reinterpret_cast<const TType*>(this) : nullptr;
}
template<typename TType> TType* as()
{
return static_cast<TileElementType>(GetType()) == TType::ElementType ? reinterpret_cast<TType*>(this) : nullptr;
}
const SurfaceElement* AsSurface() const
{
return as<SurfaceElement>();
}
SurfaceElement* AsSurface()
{
return as<SurfaceElement>();
}
const PathElement* AsPath() const
{
return as<PathElement>();
}
PathElement* AsPath()
{
return as<PathElement>();
}
const TrackElement* AsTrack() const
{
return as<TrackElement>();
}
TrackElement* AsTrack()
{
return as<TrackElement>();
}
const SmallSceneryElement* AsSmallScenery() const
{
return as<SmallSceneryElement>();
}
SmallSceneryElement* AsSmallScenery()
{
return as<SmallSceneryElement>();
}
const LargeSceneryElement* AsLargeScenery() const
{
return as<LargeSceneryElement>();
}
LargeSceneryElement* AsLargeScenery()
{
return as<LargeSceneryElement>();
}
const WallElement* AsWall() const
{
return as<WallElement>();
}
WallElement* AsWall()
{
return as<WallElement>();
}
const EntranceElement* AsEntrance() const
{
return as<EntranceElement>();
}
EntranceElement* AsEntrance()
{
return as<EntranceElement>();
}
const BannerElement* AsBanner() const
{
return as<BannerElement>();
}
BannerElement* AsBanner()
{
return as<BannerElement>();
}
};
/**
@@ -110,81 +184,6 @@ struct TileElement : public TileElementBase
uint8_t pad_05[3];
uint8_t pad_08[8];
template<typename TType, TileElementType TClass> const TType* as() const
{
return static_cast<TileElementType>(GetType()) == TClass ? reinterpret_cast<const TType*>(this) : nullptr;
}
template<typename TType, TileElementType TClass> TType* as()
{
return static_cast<TileElementType>(GetType()) == TClass ? reinterpret_cast<TType*>(this) : nullptr;
}
public:
const SurfaceElement* AsSurface() const
{
return as<SurfaceElement, TileElementType::Surface>();
}
SurfaceElement* AsSurface()
{
return as<SurfaceElement, TileElementType::Surface>();
}
const PathElement* AsPath() const
{
return as<PathElement, TileElementType::Path>();
}
PathElement* AsPath()
{
return as<PathElement, TileElementType::Path>();
}
const TrackElement* AsTrack() const
{
return as<TrackElement, TileElementType::Track>();
}
TrackElement* AsTrack()
{
return as<TrackElement, TileElementType::Track>();
}
const SmallSceneryElement* AsSmallScenery() const
{
return as<SmallSceneryElement, TileElementType::SmallScenery>();
}
SmallSceneryElement* AsSmallScenery()
{
return as<SmallSceneryElement, TileElementType::SmallScenery>();
}
const LargeSceneryElement* AsLargeScenery() const
{
return as<LargeSceneryElement, TileElementType::LargeScenery>();
}
LargeSceneryElement* AsLargeScenery()
{
return as<LargeSceneryElement, TileElementType::LargeScenery>();
}
const WallElement* AsWall() const
{
return as<WallElement, TileElementType::Wall>();
}
WallElement* AsWall()
{
return as<WallElement, TileElementType::Wall>();
}
const EntranceElement* AsEntrance() const
{
return as<EntranceElement, TileElementType::Entrance>();
}
EntranceElement* AsEntrance()
{
return as<EntranceElement, TileElementType::Entrance>();
}
const BannerElement* AsBanner() const
{
return as<BannerElement, TileElementType::Banner>();
}
BannerElement* AsBanner()
{
return as<BannerElement, TileElementType::Banner>();
}
void ClearAs(uint8_t newType);
ride_id_t GetRideIndex() const;
@@ -197,6 +196,8 @@ assert_struct_size(TileElement, 16);
struct SurfaceElement : TileElementBase
{
static constexpr TileElementType ElementType = TileElementType::Surface;
private:
uint8_t Slope;
uint8_t WaterHeight;
@@ -242,6 +243,8 @@ assert_struct_size(SurfaceElement, 16);
struct PathElement : TileElementBase
{
static constexpr TileElementType ElementType = TileElementType::Path;
private:
PathSurfaceIndex SurfaceIndex; // 5
#pragma clang diagnostic push
@@ -329,6 +332,8 @@ assert_struct_size(PathElement, 16);
struct TrackElement : TileElementBase
{
static constexpr TileElementType ElementType = TileElementType::Track;
private:
track_type_t TrackType;
union
@@ -428,6 +433,8 @@ assert_struct_size(TrackElement, 16);
struct SmallSceneryElement : TileElementBase
{
static constexpr TileElementType ElementType = TileElementType::SmallScenery;
private:
ObjectEntryIndex entryIndex; // 5
uint8_t age; // 7
@@ -459,6 +466,8 @@ assert_struct_size(SmallSceneryElement, 16);
struct LargeSceneryElement : TileElementBase
{
static constexpr TileElementType ElementType = TileElementType::LargeScenery;
private:
ObjectEntryIndex EntryIndex;
::BannerIndex BannerIndex;
@@ -494,6 +503,8 @@ assert_struct_size(LargeSceneryElement, 16);
struct WallElement : TileElementBase
{
static constexpr TileElementType ElementType = TileElementType::Wall;
private:
ObjectEntryIndex entryIndex; // 05
colour_t colour_1; // 07
@@ -537,6 +548,8 @@ assert_struct_size(WallElement, 16);
struct EntranceElement : TileElementBase
{
static constexpr TileElementType ElementType = TileElementType::Entrance;
private:
uint8_t entranceType; // 5
uint8_t SequenceIndex; // 6. Only uses the lower nibble.
@@ -568,6 +581,8 @@ assert_struct_size(EntranceElement, 16);
struct BannerElement : TileElementBase
{
static constexpr TileElementType ElementType = TileElementType::Banner;
private:
BannerIndex index; // 5
uint8_t position; // 7
@@ -594,6 +609,8 @@ assert_struct_size(BannerElement, 16);
struct CorruptElement : TileElementBase
{
static constexpr TileElementType ElementType = TileElementType::Corrupt;
uint8_t pad[3];
uint8_t pad_08[8];
};

View File

@@ -0,0 +1,130 @@
/*****************************************************************************
* Copyright (c) 2014-2021 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "Location.hpp"
#include "Map.h"
#include "TileElement.h"
#include <iterator>
namespace OpenRCT2
{
namespace Detail
{
template<typename T, typename T2> T* NextMatchingTile(T2* element)
{
if (element == nullptr)
return nullptr;
for (;;)
{
auto* res = element->template as<T>();
if (res != nullptr)
return res;
if (element->IsLastForTile())
{
break;
}
element++;
}
return nullptr;
}
} // namespace Detail
template<typename T = TileElement> class TileElementsView
{
const CoordsXY _loc;
public:
struct Iterator
{
T* element = nullptr;
Iterator& operator++()
{
if (element == nullptr)
return *this;
if (element->IsLastForTile())
{
element = nullptr;
}
else
{
element++;
if constexpr (!std::is_same_v<T, TileElement>)
{
element = Detail::NextMatchingTile<T>(element);
}
}
return *this;
}
Iterator operator++(int)
{
Iterator res = *this;
++(*this);
return res;
}
bool operator==(Iterator other) const
{
return element == other.element;
}
bool operator!=(Iterator other) const
{
return !(*this == other);
}
T* operator*()
{
return element;
}
const T* operator*() const
{
return element;
}
// iterator traits
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = const T*;
using reference = const T&;
using iterator_category = std::forward_iterator_tag;
};
TileElementsView(const CoordsXY& loc)
: _loc(loc)
{
}
Iterator begin() noexcept
{
T* element = reinterpret_cast<T*>(map_get_first_element_at(_loc));
if constexpr (!std::is_same_v<T, TileElement>)
{
element = Detail::NextMatchingTile<T>(element);
}
return Iterator{ element };
}
Iterator end() noexcept
{
return Iterator{ nullptr };
}
};
} // namespace OpenRCT2

View File

@@ -0,0 +1,180 @@
/*****************************************************************************
* Copyright (c) 2014-2021 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "TestData.h"
#include <gtest/gtest.h>
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/ParkImporter.h>
#include <openrct2/world/Footpath.h>
#include <openrct2/world/Map.h>
#include <openrct2/world/TileElementsView.h>
using namespace OpenRCT2;
class TileElementsViewTests : public testing::Test
{
protected:
static void SetUpTestCase()
{
std::string parkPath = TestData::GetParkPath("bpb.sv6");
gOpenRCT2Headless = true;
gOpenRCT2NoGraphics = true;
_context = CreateContext();
bool initialised = _context->Initialise();
ASSERT_TRUE(initialised);
load_from_sv6(parkPath.c_str());
game_load_init();
// Changed in some tests. Store to restore its value
_gScreenFlags = gScreenFlags;
SUCCEED();
}
static void TearDownTestCase()
{
if (_context)
_context.reset();
gScreenFlags = _gScreenFlags;
}
private:
static std::shared_ptr<IContext> _context;
static uint8_t _gScreenFlags;
};
std::shared_ptr<IContext> TileElementsViewTests::_context;
uint8_t TileElementsViewTests::_gScreenFlags;
template<typename T> std::vector<T*> BuildListManual(const CoordsXY& pos)
{
std::vector<TileElement*> res;
TileElement* element = map_get_first_element_at(pos);
if (element == nullptr)
return res;
do
{
if constexpr (!std::is_same_v<T, TileElement>)
{
auto* res = element->as<T>();
if (res)
res.push_back(res);
}
else
{
res.push_back(element);
}
} while (!(element++)->IsLastForTile());
return res;
}
template<typename T> std::vector<T*> BuildListByView(const CoordsXY& pos)
{
std::vector<TileElement*> res;
for (auto* element : TileElementsView<T>(pos))
{
res.push_back(element);
}
return res;
}
template<typename T> bool CompareLists(const CoordsXY& pos)
{
auto listManual = BuildListManual<TileElement>(pos);
auto listView = BuildListByView<TileElement>(pos);
EXPECT_EQ(listManual.size(), listView.size());
if (listManual.size() != listView.size())
return false;
for (size_t i = 0; i < listManual.size(); ++i)
{
EXPECT_EQ(listManual[i], listView[i]) << "[i] = " << i;
if (listManual[i] != listView[i])
return false;
}
return true;
}
template<typename T> void CheckMapTiles()
{
for (int x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; ++x)
{
for (int y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; ++y)
{
auto pos = TileCoordsXY(x, y).ToCoordsXY();
bool matches = CompareLists<T>(pos);
EXPECT_TRUE(matches) << "x = " << x << ", y = " << y;
if (!matches)
{
FAIL();
}
}
}
SUCCEED();
}
TEST_F(TileElementsViewTests, QueryTypeGeneric)
{
CheckMapTiles<TileElement>();
}
TEST_F(TileElementsViewTests, QueryTypePathElements)
{
CheckMapTiles<PathElement>();
}
TEST_F(TileElementsViewTests, QueryTypeSurfaceElements)
{
CheckMapTiles<SurfaceElement>();
}
TEST_F(TileElementsViewTests, QueryTypeTrackElements)
{
CheckMapTiles<TrackElement>();
}
TEST_F(TileElementsViewTests, QueryTypeSmallSceneryElements)
{
CheckMapTiles<SmallSceneryElement>();
}
TEST_F(TileElementsViewTests, QueryTypeLargeSceneryElements)
{
CheckMapTiles<LargeSceneryElement>();
}
TEST_F(TileElementsViewTests, QueryTypeWallElements)
{
CheckMapTiles<WallElement>();
}
TEST_F(TileElementsViewTests, QueryTypeEntranceElements)
{
CheckMapTiles<EntranceElement>();
}
TEST_F(TileElementsViewTests, QueryTypeBannerElements)
{
CheckMapTiles<BannerElement>();
}

View File

@@ -78,6 +78,7 @@
<ClCompile Include="tests.cpp" />
<ClCompile Include="StringTest.cpp" />
<ClCompile Include="TileElements.cpp" />
<ClCompile Include="TileElementsView.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="testdata\sprites\badManifest.json" />