From 6186766a0532839a213b3ad913f2eb820d3afdea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= Date: Thu, 4 Feb 2021 18:58:45 +0200 Subject: [PATCH] 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 --- src/openrct2/libopenrct2.vcxproj | 1 + src/openrct2/world/Map.cpp | 59 ++------- src/openrct2/world/TileElement.h | 167 +++++++++++++----------- src/openrct2/world/TileElementsView.h | 130 +++++++++++++++++++ test/tests/TileElementsView.cpp | 180 ++++++++++++++++++++++++++ test/tests/tests.vcxproj | 1 + 6 files changed, 418 insertions(+), 120 deletions(-) create mode 100644 src/openrct2/world/TileElementsView.h create mode 100644 test/tests/TileElementsView.cpp diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index a8fa569548..d106053dd4 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -453,6 +453,7 @@ + diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index ee7f92d6ec..8c6e99f95a 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -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(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(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(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; } diff --git a/src/openrct2/world/TileElement.h b/src/openrct2/world/TileElement.h index 04dd468635..905674ac42 100644 --- a/src/openrct2/world/TileElement.h +++ b/src/openrct2/world/TileElement.h @@ -99,6 +99,80 @@ struct TileElementBase uint8_t GetOwner() const; void SetOwner(uint8_t newOwner); + + template const TType* as() const + { + return static_cast(GetType()) == TType::ElementType ? reinterpret_cast(this) : nullptr; + } + template TType* as() + { + return static_cast(GetType()) == TType::ElementType ? reinterpret_cast(this) : nullptr; + } + + const SurfaceElement* AsSurface() const + { + return as(); + } + SurfaceElement* AsSurface() + { + return as(); + } + const PathElement* AsPath() const + { + return as(); + } + PathElement* AsPath() + { + return as(); + } + const TrackElement* AsTrack() const + { + return as(); + } + TrackElement* AsTrack() + { + return as(); + } + const SmallSceneryElement* AsSmallScenery() const + { + return as(); + } + SmallSceneryElement* AsSmallScenery() + { + return as(); + } + const LargeSceneryElement* AsLargeScenery() const + { + return as(); + } + LargeSceneryElement* AsLargeScenery() + { + return as(); + } + const WallElement* AsWall() const + { + return as(); + } + WallElement* AsWall() + { + return as(); + } + const EntranceElement* AsEntrance() const + { + return as(); + } + EntranceElement* AsEntrance() + { + return as(); + } + const BannerElement* AsBanner() const + { + return as(); + } + BannerElement* AsBanner() + { + return as(); + } }; /** @@ -110,81 +184,6 @@ struct TileElement : public TileElementBase uint8_t pad_05[3]; uint8_t pad_08[8]; - template const TType* as() const - { - return static_cast(GetType()) == TClass ? reinterpret_cast(this) : nullptr; - } - template TType* as() - { - return static_cast(GetType()) == TClass ? reinterpret_cast(this) : nullptr; - } - -public: - const SurfaceElement* AsSurface() const - { - return as(); - } - SurfaceElement* AsSurface() - { - return as(); - } - const PathElement* AsPath() const - { - return as(); - } - PathElement* AsPath() - { - return as(); - } - const TrackElement* AsTrack() const - { - return as(); - } - TrackElement* AsTrack() - { - return as(); - } - const SmallSceneryElement* AsSmallScenery() const - { - return as(); - } - SmallSceneryElement* AsSmallScenery() - { - return as(); - } - const LargeSceneryElement* AsLargeScenery() const - { - return as(); - } - LargeSceneryElement* AsLargeScenery() - { - return as(); - } - const WallElement* AsWall() const - { - return as(); - } - WallElement* AsWall() - { - return as(); - } - const EntranceElement* AsEntrance() const - { - return as(); - } - EntranceElement* AsEntrance() - { - return as(); - } - const BannerElement* AsBanner() const - { - return as(); - } - BannerElement* AsBanner() - { - return as(); - } - 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]; }; diff --git a/src/openrct2/world/TileElementsView.h b/src/openrct2/world/TileElementsView.h new file mode 100644 index 0000000000..d43e995999 --- /dev/null +++ b/src/openrct2/world/TileElementsView.h @@ -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 + +namespace OpenRCT2 +{ + namespace Detail + { + template T* NextMatchingTile(T2* element) + { + if (element == nullptr) + return nullptr; + + for (;;) + { + auto* res = element->template as(); + if (res != nullptr) + return res; + + if (element->IsLastForTile()) + { + break; + } + element++; + } + + return nullptr; + } + } // namespace Detail + + template 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) + { + element = Detail::NextMatchingTile(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(map_get_first_element_at(_loc)); + + if constexpr (!std::is_same_v) + { + element = Detail::NextMatchingTile(element); + } + + return Iterator{ element }; + } + + Iterator end() noexcept + { + return Iterator{ nullptr }; + } + }; + +} // namespace OpenRCT2 diff --git a/test/tests/TileElementsView.cpp b/test/tests/TileElementsView.cpp new file mode 100644 index 0000000000..d70cb57efa --- /dev/null +++ b/test/tests/TileElementsView.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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 _context; + static uint8_t _gScreenFlags; +}; + +std::shared_ptr TileElementsViewTests::_context; +uint8_t TileElementsViewTests::_gScreenFlags; + +template std::vector BuildListManual(const CoordsXY& pos) +{ + std::vector res; + + TileElement* element = map_get_first_element_at(pos); + if (element == nullptr) + return res; + + do + { + if constexpr (!std::is_same_v) + { + auto* res = element->as(); + if (res) + res.push_back(res); + } + else + { + res.push_back(element); + } + + } while (!(element++)->IsLastForTile()); + + return res; +} + +template std::vector BuildListByView(const CoordsXY& pos) +{ + std::vector res; + + for (auto* element : TileElementsView(pos)) + { + res.push_back(element); + } + + return res; +} + +template bool CompareLists(const CoordsXY& pos) +{ + auto listManual = BuildListManual(pos); + auto listView = BuildListByView(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 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(pos); + EXPECT_TRUE(matches) << "x = " << x << ", y = " << y; + + if (!matches) + { + FAIL(); + } + } + } + SUCCEED(); +} + +TEST_F(TileElementsViewTests, QueryTypeGeneric) +{ + CheckMapTiles(); +} + +TEST_F(TileElementsViewTests, QueryTypePathElements) +{ + CheckMapTiles(); +} + +TEST_F(TileElementsViewTests, QueryTypeSurfaceElements) +{ + CheckMapTiles(); +} + +TEST_F(TileElementsViewTests, QueryTypeTrackElements) +{ + CheckMapTiles(); +} + +TEST_F(TileElementsViewTests, QueryTypeSmallSceneryElements) +{ + CheckMapTiles(); +} + +TEST_F(TileElementsViewTests, QueryTypeLargeSceneryElements) +{ + CheckMapTiles(); +} + +TEST_F(TileElementsViewTests, QueryTypeWallElements) +{ + CheckMapTiles(); +} + +TEST_F(TileElementsViewTests, QueryTypeEntranceElements) +{ + CheckMapTiles(); +} + +TEST_F(TileElementsViewTests, QueryTypeBannerElements) +{ + CheckMapTiles(); +} diff --git a/test/tests/tests.vcxproj b/test/tests/tests.vcxproj index d192c57318..53a49a7ca0 100644 --- a/test/tests/tests.vcxproj +++ b/test/tests/tests.vcxproj @@ -78,6 +78,7 @@ +