diff --git a/Source/controls/plrctrls.cpp b/Source/controls/plrctrls.cpp index bce9b0169..844d06afc 100644 --- a/Source/controls/plrctrls.cpp +++ b/Source/controls/plrctrls.cpp @@ -150,12 +150,12 @@ int GetDistanceRanged(Point destination) void FindItemOrObject() { - Point futurePosition = MyPlayer->position.future; + WorldTilePosition futurePosition = MyPlayer->position.future; int rotations = 5; - auto searchArea = PointsInRectangleRangeColMajor { Rectangle { futurePosition, 1 } }; + auto searchArea = PointsInRectangleColMajor(WorldTileRectangle { futurePosition, 1 }); - for (Point targetPosition : searchArea) { + for (WorldTilePosition targetPosition : searchArea) { // As the player can not stand on the edge of the map this is safe from OOB int8_t itemId = dItem[targetPosition.x][targetPosition.y] - 1; if (itemId < 0) { @@ -185,7 +185,7 @@ void FindItemOrObject() return; // Don't look for objects in town } - for (Point targetPosition : searchArea) { + for (WorldTilePosition targetPosition : searchArea) { Object *object = FindObjectAtPosition(targetPosition); if (object == nullptr || object->_oSelFlag == 0) { // No object or non-interactive object @@ -701,7 +701,7 @@ Point FindFirstStashSlotOnItem(StashStruct::StashCell itemInvId) if (itemInvId == StashStruct::EmptyCell) return InvalidStashPoint; - for (auto point : PointsInRectangleRange({ { 0, 0 }, Size { 10, 10 } })) { + for (WorldTilePosition point : PointsInRectangle(WorldTileRectangle { { 0, 0 }, { 10, 10 } })) { if (Stash.GetItemIdAtPosition(point) == itemInvId) return point; } @@ -770,7 +770,7 @@ Point FindClosestStashSlot(Point mousePos) Point bestSlot = {}; mousePos += Displacement { -INV_SLOT_HALF_SIZE_PX, -INV_SLOT_HALF_SIZE_PX }; - for (auto point : PointsInRectangleRange({ { 0, 0 }, Size { 10, 10 } })) { + for (Point point : PointsInRectangle(Rectangle { { 0, 0 }, Size { 10, 10 } })) { int distance = mousePos.ManhattanDistance(GetStashSlotCoord(point)); if (distance < shortestDistance) { shortestDistance = distance; @@ -1781,7 +1781,7 @@ void PerformPrimaryAction() StashStruct::StashCell itemUnderCursor = [](Point stashSlot, Size cursorSizeInCells) -> StashStruct::StashCell { if (stashSlot != InvalidStashPoint) return StashStruct::EmptyCell; - for (Point slotUnderCursor : PointsInRectangleRange { { stashSlot, cursorSizeInCells } }) { + for (Point slotUnderCursor : PointsInRectangle(Rectangle { stashSlot, cursorSizeInCells })) { if (slotUnderCursor.x >= 10 || slotUnderCursor.y >= 10) continue; StashStruct::StashCell itemId = Stash.GetItemIdAtPosition(slotUnderCursor); diff --git a/Source/engine/displacement.hpp b/Source/engine/displacement.hpp index 6d04d5a51..c30bf4c9c 100644 --- a/Source/engine/displacement.hpp +++ b/Source/engine/displacement.hpp @@ -5,8 +5,8 @@ #include #endif -#include "direction.hpp" -#include "size.hpp" +#include "engine/direction.hpp" +#include "engine/size.hpp" #include "utils/stdcompat/abs.hpp" namespace devilution { @@ -42,7 +42,8 @@ struct DisplacementOf { { } - explicit constexpr DisplacementOf(const Size &size) + template + explicit constexpr DisplacementOf(const SizeOf &size) : deltaX(size.width) , deltaY(size.height) { diff --git a/Source/engine/point.hpp b/Source/engine/point.hpp index f6486761c..84ced2577 100644 --- a/Source/engine/point.hpp +++ b/Source/engine/point.hpp @@ -151,7 +151,7 @@ struct PointOf { */ constexpr PointOf megaToWorld() const { - return { 16 + 2 * x, 16 + 2 * y }; + return { static_cast(16 + 2 * x), static_cast(16 + 2 * y) }; } /** @@ -159,7 +159,7 @@ struct PointOf { */ constexpr PointOf worldToMega() const { - return { (x - 16) / 2, (y - 16) / 2 }; + return { static_cast((x - 16) / 2), static_cast((y - 16) / 2) }; } }; diff --git a/Source/engine/points_in_rectangle_range.hpp b/Source/engine/points_in_rectangle_range.hpp index 9e6e81593..2251c1f10 100644 --- a/Source/engine/points_in_rectangle_range.hpp +++ b/Source/engine/points_in_rectangle_range.hpp @@ -7,16 +7,17 @@ namespace devilution { +template class PointsInRectangleIteratorBase { public: using iterator_category = std::random_access_iterator_tag; - using difference_type = int; - using value_type = Point; + using difference_type = CoordT; + using value_type = PointOf; using pointer = void; using reference = value_type; protected: - constexpr PointsInRectangleIteratorBase(Point origin, int majorDimension, int majorIndex, int minorIndex) + constexpr PointsInRectangleIteratorBase(PointOf origin, int majorDimension, int majorIndex, int minorIndex) : origin(origin) , majorDimension(majorDimension) , majorIndex(majorIndex) @@ -24,7 +25,7 @@ protected: { } - explicit constexpr PointsInRectangleIteratorBase(Point origin, int majorDimension, int index = 0) + explicit constexpr PointsInRectangleIteratorBase(PointOf origin, int majorDimension, int index = 0) : PointsInRectangleIteratorBase(origin, majorDimension, index / majorDimension, index % majorDimension) { } @@ -57,7 +58,7 @@ protected: } } - Point origin; + PointOf origin; int majorDimension; @@ -65,19 +66,26 @@ protected: int minorIndex; }; +template class PointsInRectangleRange { public: - using const_iterator = class PointsInRectangleIterator : public PointsInRectangleIteratorBase { + using const_iterator = class PointsInRectangleIterator : public PointsInRectangleIteratorBase { public: - constexpr PointsInRectangleIterator(Rectangle region, int index = 0) - : PointsInRectangleIteratorBase(region.position, region.size.width, index) + using iterator_category = typename PointsInRectangleIteratorBase::iterator_category; + using difference_type = typename PointsInRectangleIteratorBase::difference_type; + using value_type = typename PointsInRectangleIteratorBase::value_type; + using pointer = typename PointsInRectangleIteratorBase::pointer; + using reference = typename PointsInRectangleIteratorBase::reference; + + constexpr PointsInRectangleIterator(RectangleOf region, int index = 0) + : PointsInRectangleIteratorBase(region.position, region.size.width, index) { } value_type operator*() const { // Row-major iteration e.g. {0, 0}, {1, 0}, {2, 0}, {0, 1}, {1, 1}, ... - return origin + Displacement { minorIndex, majorIndex }; + return this->origin + Displacement { this->minorIndex, this->majorIndex }; } // Equality comparable concepts @@ -114,13 +122,13 @@ public: difference_type operator-(const PointsInRectangleIterator &rhs) const { - return (this->majorIndex - rhs.majorIndex) * majorDimension + (this->minorIndex - rhs.minorIndex); + return (this->majorIndex - rhs.majorIndex) * this->majorDimension + (this->minorIndex - rhs.minorIndex); } // Forward concepts PointsInRectangleIterator &operator++() { - Increment(); + this->Increment(); return *this; } @@ -134,7 +142,7 @@ public: // Bidirectional concepts PointsInRectangleIterator &operator--() { - Decrement(); + this->Decrement(); return *this; } @@ -154,7 +162,7 @@ public: PointsInRectangleIterator &operator+=(difference_type delta) { - Offset(delta); + this->Offset(delta); return *this; } @@ -180,7 +188,7 @@ public: } }; - constexpr PointsInRectangleRange(Rectangle region) + constexpr PointsInRectangleRange(RectangleOf region) : region(region) { } @@ -228,22 +236,35 @@ public: } protected: - Rectangle region; + RectangleOf region; }; -class PointsInRectangleRangeColMajor { +template +PointsInRectangleRange PointsInRectangle(RectangleOf region) +{ + return PointsInRectangleRange { region }; +} + +template +class PointsInRectangleColMajorRange { public: - using const_iterator = class PointsInRectangleIteratorColMajor : public PointsInRectangleIteratorBase { + using const_iterator = class PointsInRectangleIteratorColMajor : public PointsInRectangleIteratorBase { public: - constexpr PointsInRectangleIteratorColMajor(Rectangle region, int index = 0) - : PointsInRectangleIteratorBase(region.position, region.size.height, index) + using iterator_category = typename PointsInRectangleIteratorBase::iterator_category; + using difference_type = typename PointsInRectangleIteratorBase::difference_type; + using value_type = typename PointsInRectangleIteratorBase::value_type; + using pointer = typename PointsInRectangleIteratorBase::pointer; + using reference = typename PointsInRectangleIteratorBase::reference; + + constexpr PointsInRectangleIteratorColMajor(RectangleOf region, int index = 0) + : PointsInRectangleIteratorBase(region.position, region.size.height, index) { } value_type operator*() const { // Col-major iteration e.g. {0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, ... - return origin + Displacement { majorIndex, minorIndex }; + return this->origin + Displacement { this->majorIndex, this->minorIndex }; } // Equality comparable concepts @@ -280,13 +301,13 @@ public: difference_type operator-(const PointsInRectangleIteratorColMajor &rhs) const { - return (this->majorIndex - rhs.majorIndex) * majorDimension + (this->minorIndex - rhs.minorIndex); + return (this->majorIndex - rhs.majorIndex) * this->majorDimension + (this->minorIndex - rhs.minorIndex); } // Forward concepts PointsInRectangleIteratorColMajor &operator++() { - Increment(); + this->Increment(); return *this; } @@ -300,7 +321,7 @@ public: // Bidirectional concepts PointsInRectangleIteratorColMajor &operator--() { - Decrement(); + this->Decrement(); return *this; } @@ -320,7 +341,7 @@ public: PointsInRectangleIteratorColMajor &operator+=(difference_type delta) { - Offset(delta); + this->Offset(delta); return *this; } @@ -347,7 +368,7 @@ public: }; // gcc6 needs a defined constructor? - constexpr PointsInRectangleRangeColMajor(Rectangle region) + constexpr PointsInRectangleColMajorRange(RectangleOf region) : region(region) { } @@ -395,7 +416,13 @@ public: } protected: - Rectangle region; + RectangleOf region; }; +template +PointsInRectangleColMajorRange PointsInRectangleColMajor(RectangleOf region) +{ + return PointsInRectangleColMajorRange { region }; +} + } // namespace devilution diff --git a/Source/engine/rectangle.hpp b/Source/engine/rectangle.hpp index 22b9e907a..bcc840a97 100644 --- a/Source/engine/rectangle.hpp +++ b/Source/engine/rectangle.hpp @@ -5,13 +5,14 @@ namespace devilution { -struct Rectangle { - Point position; - Size size; +template +struct RectangleOf { + PointOf position; + SizeOf size; - Rectangle() = default; + RectangleOf() = default; - constexpr Rectangle(Point position, Size size) + constexpr RectangleOf(PointOf position, SizeOf size) : position(position) , size(size) { @@ -25,26 +26,37 @@ struct Rectangle { * @param center center point of the target rectangle * @param radius a non-negative value indicating how many tiles to include around the center */ - explicit constexpr Rectangle(Point center, int radius) - : position(center - Displacement { radius }) - , size(2 * radius + 1) + explicit constexpr RectangleOf(PointOf center, SizeT radius) + : position(center - DisplacementOf { radius }) + , size(static_cast(2 * radius + 1)) { } - constexpr bool contains(Point point) const + /** + * @brief Whether this rectangle contains the given point. + * Works correctly even if the point uses a different underlying numeric type + */ + template + constexpr bool contains(PointOf point) const + { + return contains(point.x, point.y); + } + + template + constexpr bool contains(T x, T y) const { - return point.x >= this->position.x - && point.x < (this->position.x + this->size.width) - && point.y >= this->position.y - && point.y < (this->position.y + this->size.height); + return x >= this->position.x + && x < (this->position.x + this->size.width) + && y >= this->position.y + && y < (this->position.y + this->size.height); } /** * @brief Computes the center of this rectangle in integer coordinates. Values are truncated towards zero. */ - constexpr Point Center() const + constexpr PointOf Center() const { - return position + Displacement(size / 2); + return position + DisplacementOf(size / 2); } /** @@ -52,13 +64,15 @@ struct Rectangle { * * Effectively moves the left/right edges in by deltaX, and the top/bottom edges in by deltaY */ - constexpr Rectangle inset(Displacement factor) const + constexpr RectangleOf inset(DisplacementOf factor) const { return { position + factor, - Size { size.width - factor.deltaX * 2, size.height - factor.deltaY * 2 } + SizeOf(size.width - factor.deltaX * 2, size.height - factor.deltaY * 2) }; } }; +using Rectangle = RectangleOf; + } // namespace devilution diff --git a/Source/engine/size.hpp b/Source/engine/size.hpp index a53f23fca..1750a8cc0 100644 --- a/Source/engine/size.hpp +++ b/Source/engine/size.hpp @@ -6,86 +6,87 @@ namespace devilution { -struct Size { - int width; - int height; +template +struct SizeOf { + SizeT width; + SizeT height; - Size() = default; + SizeOf() = default; - constexpr Size(int width, int height) + constexpr SizeOf(SizeT width, SizeT height) : width(width) , height(height) { } - explicit constexpr Size(int size) + explicit constexpr SizeOf(SizeT size) : width(size) , height(size) { } - bool operator==(const Size &other) const + bool operator==(const SizeOf &other) const { return width == other.width && height == other.height; } - bool operator!=(const Size &other) const + bool operator!=(const SizeOf &other) const { return !(*this == other); } - constexpr Size &operator+=(const int factor) + constexpr SizeOf &operator+=(SizeT factor) { width += factor; height += factor; return *this; } - constexpr Size &operator-=(const int factor) + constexpr SizeOf &operator-=(SizeT factor) { return *this += -factor; } - constexpr Size &operator*=(const int factor) + constexpr SizeOf &operator*=(SizeT factor) { width *= factor; height *= factor; return *this; } - constexpr Size &operator*=(const float factor) + constexpr SizeOf &operator*=(float factor) { - width = static_cast(width * factor); - height = static_cast(height * factor); + width = static_cast(width * factor); + height = static_cast(height * factor); return *this; } - constexpr Size &operator/=(const int factor) + constexpr SizeOf &operator/=(SizeT factor) { width /= factor; height /= factor; return *this; } - constexpr friend Size operator+(Size a, const int factor) + constexpr friend SizeOf operator+(SizeOf a, SizeT factor) { a += factor; return a; } - constexpr friend Size operator-(Size a, const int factor) + constexpr friend SizeOf operator-(SizeOf a, SizeT factor) { a -= factor; return a; } - constexpr friend Size operator*(Size a, const int factor) + constexpr friend SizeOf operator*(SizeOf a, SizeT factor) { a *= factor; return a; } - constexpr friend Size operator/(Size a, const int factor) + constexpr friend SizeOf operator/(SizeOf a, SizeT factor) { a /= factor; return a; @@ -98,11 +99,13 @@ struct Size { * @param size Object to display * @return the stream, to allow chaining */ - friend std::ostream &operator<<(std::ostream &stream, const Size &size) + friend std::ostream &operator<<(std::ostream &stream, const SizeOf &size) { return stream << "(width: " << size.width << ", height: " << size.height << ")"; } #endif }; +using Size = SizeOf; + } // namespace devilution diff --git a/Source/engine/world_tile.hpp b/Source/engine/world_tile.hpp index bacdc643f..fcce9173c 100644 --- a/Source/engine/world_tile.hpp +++ b/Source/engine/world_tile.hpp @@ -1,6 +1,8 @@ #pragma once #include "engine/point.hpp" +#include "engine/rectangle.hpp" +#include "engine/size.hpp" namespace devilution { @@ -10,6 +12,9 @@ using WorldTilePosition = PointOf; using WorldTileOffset = int8_t; using WorldTileDisplacement = DisplacementOf; +using WorldTileSize = SizeOf; +using WorldTileRectangle = RectangleOf; + } // namespace devilution namespace std { diff --git a/Source/levels/crypt.cpp b/Source/levels/crypt.cpp index 8a6412e89..fd942d2ab 100644 --- a/Source/levels/crypt.cpp +++ b/Source/levels/crypt.cpp @@ -686,7 +686,7 @@ void SetCryptRoom() auto dunData = LoadFileInMem("nlevels\\l5data\\uberroom.dun"); - SetPiece = { position, { dunData[0], dunData[1] } }; + SetPiece = { position, WorldTileSize(SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1])) }; PlaceDunTiles(dunData.get(), position, 0); } @@ -697,7 +697,7 @@ void SetCornerRoom() auto dunData = LoadFileInMem("nlevels\\l5data\\cornerstone.dun"); - SetPiece = { position, { dunData[0], dunData[1] } }; + SetPiece = { position, WorldTileSize(SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1])) }; PlaceDunTiles(dunData.get(), position, 0); } diff --git a/Source/levels/drlg_l1.cpp b/Source/levels/drlg_l1.cpp index d9028c39f..3b76bbc9a 100644 --- a/Source/levels/drlg_l1.cpp +++ b/Source/levels/drlg_l1.cpp @@ -456,7 +456,9 @@ void GenerateRoom(Rectangle area, bool verticalLayout) Rectangle room1; for (int num = 0; num < 20; num++) { - room1.size = { (GenerateRnd(5) + 2) & ~1, (GenerateRnd(5) + 2) & ~1 }; + const int32_t randomWidth = (GenerateRnd(5) + 2) & ~1; + const int32_t randomHeight = (GenerateRnd(5) + 2) & ~1; + room1.size = { randomWidth, randomHeight }; room1.position = area.position; if (verticalLayout) { room1.position += Displacement { -room1.size.width, area.size.height / 2 - room1.size.height / 2 }; @@ -1239,11 +1241,11 @@ void Pass3() void PlaceMiniSetRandom(const Miniset &miniset, int rndper) { - int sw = miniset.size.width; - int sh = miniset.size.height; + const WorldTileCoord sw = miniset.size.width; + const WorldTileCoord sh = miniset.size.height; - for (int sy = 0; sy < DMAXY - sh; sy++) { - for (int sx = 0; sx < DMAXX - sw; sx++) { + for (WorldTileCoord sy = 0; sy < DMAXY - sh; sy++) { + for (WorldTileCoord sx = 0; sx < DMAXX - sw; sx++) { if (!miniset.matches({ sx, sy }, false)) continue; // BUGFIX: This code is copied from Cave and should not be applied for crypt @@ -1256,7 +1258,7 @@ void PlaceMiniSetRandom(const Miniset &miniset, int rndper) } } -Point SelectChamber() +WorldTilePosition SelectChamber() { int chamber; if (HasChamber1 && HasChamber2 && HasChamber3) { @@ -1276,9 +1278,9 @@ Point SelectChamber() switch (chamber) { case 1: - return VerticalLayout ? Point { 16, 2 } : Point { 2, 16 }; + return VerticalLayout ? WorldTilePosition { 16, 2 } : WorldTilePosition { 2, 16 }; case 3: - return VerticalLayout ? Point { 16, 30 } : Point { 30, 16 }; + return VerticalLayout ? WorldTilePosition { 16, 30 } : WorldTilePosition { 30, 16 }; default: return { 16, 16 }; } diff --git a/Source/levels/drlg_l1.h b/Source/levels/drlg_l1.h index b2c71defe..c2480e3fe 100644 --- a/Source/levels/drlg_l1.h +++ b/Source/levels/drlg_l1.h @@ -5,12 +5,13 @@ */ #pragma once +#include "engine/world_tile.hpp" #include "levels/gendung.h" namespace devilution { void PlaceMiniSetRandom(const Miniset &miniset, int rndper); -Point SelectChamber(); +WorldTilePosition SelectChamber(); void CreateL5Dungeon(uint32_t rseed, lvl_entry entry); void LoadPreL1Dungeon(const char *path); void LoadL1Dungeon(const char *path, Point spawn); diff --git a/Source/levels/drlg_l2.cpp b/Source/levels/drlg_l2.cpp index a6fb3288b..aaef87892 100644 --- a/Source/levels/drlg_l2.cpp +++ b/Source/levels/drlg_l2.cpp @@ -1595,12 +1595,12 @@ void ApplyShadowsPatterns() void PlaceMiniSetRandom(const Miniset &miniset, int rndper) { - int sw = miniset.size.width; - int sh = miniset.size.height; + const WorldTileCoord sw = miniset.size.width; + const WorldTileCoord sh = miniset.size.height; - for (int sy = 0; sy < DMAXY - sh; sy++) { - for (int sx = 0; sx < DMAXX - sw; sx++) { - if (SetPieceRoom.contains({ sx, sy })) + for (WorldTileCoord sy = 0; sy < DMAXY - sh; sy++) { + for (WorldTileCoord sx = 0; sx < DMAXX - sw; sx++) { + if (SetPieceRoom.contains(sx, sy)) continue; if (!miniset.matches({ sx, sy })) continue; @@ -1752,21 +1752,18 @@ void PlaceHallExt(Point position) * @param nHDir The direction of the hall from nRDest to this room. * @param size If set, is is used used for room size instead of random values. */ -void CreateRoom(Point topLeft, Point bottomRight, int nRDest, HallDirection nHDir, std::optional size) +void CreateRoom(WorldTilePosition topLeft, WorldTilePosition bottomRight, int nRDest, HallDirection nHDir, std::optional size) { - if (nRoomCnt >= 80) - return; - - Displacement areaDisplacement = bottomRight - topLeft; - Size area { areaDisplacement.deltaX, areaDisplacement.deltaY }; - constexpr int AreaMin = 2; - if (area.width < AreaMin || area.height < AreaMin) + if (nRoomCnt >= 80 || topLeft.x + AreaMin > bottomRight.x || topLeft.y + AreaMin > bottomRight.y) return; - constexpr int RoomMax = 10; - constexpr int RoomMin = 4; - Size roomSize = area; + WorldTileDisplacement areaDisplacement = bottomRight - topLeft; + WorldTileSize area(areaDisplacement.deltaX, areaDisplacement.deltaY); + + constexpr WorldTileCoord RoomMax = 10; + constexpr WorldTileCoord RoomMin = 4; + WorldTileSize roomSize = area; if (area.width > RoomMin) roomSize.width = GenerateRnd(std::min(area.width, RoomMax) - RoomMin) + RoomMin; if (area.height > RoomMin) @@ -1775,8 +1772,11 @@ void CreateRoom(Point topLeft, Point bottomRight, int nRDest, HallDirection nHDi if (size) roomSize = *size; - Point roomTopLeft = topLeft + Displacement { GenerateRnd(area.width), GenerateRnd(area.height) }; - Point roomBottomRight = roomTopLeft + Displacement { roomSize }; + const int32_t randomWidth = GenerateRnd(area.width); + const int32_t randomHeight = GenerateRnd(area.height); + + WorldTilePosition roomTopLeft = topLeft + WorldTileDisplacement(randomWidth, randomHeight); + WorldTilePosition roomBottomRight = roomTopLeft + WorldTileDisplacement(roomSize); if (roomBottomRight.x > bottomRight.x) { roomBottomRight.x = bottomRight.x; roomTopLeft.x = bottomRight.x - roomSize.width; @@ -1786,14 +1786,14 @@ void CreateRoom(Point topLeft, Point bottomRight, int nRDest, HallDirection nHDi roomTopLeft.y = bottomRight.y - roomSize.height; } - roomTopLeft.x = clamp(roomTopLeft.x, 1, 38); - roomTopLeft.y = clamp(roomTopLeft.y, 1, 38); - roomBottomRight.x = clamp(roomBottomRight.x, 1, 38); - roomBottomRight.y = clamp(roomBottomRight.y, 1, 38); + roomTopLeft.x = clamp(roomTopLeft.x, 1, 38); + roomTopLeft.y = clamp(roomTopLeft.y, 1, 38); + roomBottomRight.x = clamp(roomBottomRight.x, 1, 38); + roomBottomRight.y = clamp(roomBottomRight.y, 1, 38); DefineRoom(roomTopLeft, roomBottomRight, static_cast(size)); - constexpr Displacement standoff { 2, 2 }; + constexpr WorldTileDisplacement standoff { 2, 2 }; if (size) SetPieceRoom = { roomTopLeft + standoff, roomSize - 1 }; @@ -1835,18 +1835,18 @@ void CreateRoom(Point topLeft, Point bottomRight, int nRDest, HallDirection nHDi HallList.push_back({ { nHx1, nHy1 }, { nHx2, nHy2 }, nHDir }); } - Point roomBottomLeft { roomTopLeft.x, roomBottomRight.y }; - Point roomTopRight { roomBottomRight.x, roomTopLeft.y }; + WorldTilePosition roomBottomLeft { roomTopLeft.x, roomBottomRight.y }; + WorldTilePosition roomTopRight { roomBottomRight.x, roomTopLeft.y }; if (roomSize.height > roomSize.width) { CreateRoom(topLeft + standoff, roomBottomLeft - standoff, nRid, HallDirection::Right, {}); CreateRoom(roomTopRight + standoff, bottomRight - standoff, nRid, HallDirection::Left, {}); - CreateRoom(Point { topLeft.x, roomBottomRight.y } + standoff, Point { roomBottomRight.x, bottomRight.y } - standoff, nRid, HallDirection::Up, {}); - CreateRoom(Point { roomTopLeft.x, topLeft.y } + standoff, Point { bottomRight.x, roomTopLeft.y } - standoff, nRid, HallDirection::Down, {}); + CreateRoom(WorldTilePosition { topLeft.x, roomBottomRight.y } + standoff, WorldTilePosition { roomBottomRight.x, bottomRight.y } - standoff, nRid, HallDirection::Up, {}); + CreateRoom(WorldTilePosition { roomTopLeft.x, topLeft.y } + standoff, WorldTilePosition { bottomRight.x, roomTopLeft.y } - standoff, nRid, HallDirection::Down, {}); } else { CreateRoom(topLeft + standoff, roomTopRight - standoff, nRid, HallDirection::Down, {}); CreateRoom(roomBottomLeft + standoff, bottomRight - standoff, nRid, HallDirection::Up, {}); - CreateRoom(Point { topLeft.x, roomTopLeft.y } + standoff, Point { roomTopLeft.x, bottomRight.y } - standoff, nRid, HallDirection::Right, {}); - CreateRoom(Point { roomBottomRight.x, topLeft.y } + standoff, Point { bottomRight.x, roomBottomRight.y } - standoff, nRid, HallDirection::Left, {}); + CreateRoom(WorldTilePosition { topLeft.x, roomTopLeft.y } + standoff, WorldTilePosition { roomTopLeft.x, bottomRight.y } - standoff, nRid, HallDirection::Right, {}); + CreateRoom(WorldTilePosition { roomBottomRight.x, topLeft.y } + standoff, WorldTilePosition { bottomRight.x, roomBottomRight.y } - standoff, nRid, HallDirection::Left, {}); } } @@ -2059,9 +2059,9 @@ void FixTilesPatterns() void Substitution() { - for (int y = 0; y < DMAXY; y++) { - for (int x = 0; x < DMAXX; x++) { - if (SetPieceRoom.contains({ x, y })) + for (WorldTileCoord y = 0; y < DMAXY; y++) { + for (WorldTileCoord x = 0; x < DMAXX; x++) { + if (SetPieceRoom.contains(x, y)) continue; if (!FlipCoin(4)) continue; @@ -2425,7 +2425,7 @@ bool FillVoids() bool CreateDungeon() { - std::optional size; + std::optional size; switch (currlevel) { case 5: diff --git a/Source/levels/drlg_l3.cpp b/Source/levels/drlg_l3.cpp index 58d5d1eda..34f4148b7 100644 --- a/Source/levels/drlg_l3.cpp +++ b/Source/levels/drlg_l3.cpp @@ -1387,12 +1387,12 @@ bool CanReplaceTile(uint8_t replace, Point tile) */ bool PlaceMiniSetRandom(const Miniset &miniset, int rndper) { - int sw = miniset.size.width; - int sh = miniset.size.height; + const WorldTileCoord sw = miniset.size.width; + const WorldTileCoord sh = miniset.size.height; bool placed = false; - for (int sy = 0; sy < DMAXY - sh; sy++) { - for (int sx = 0; sx < DMAXX - sw; sx++) { + for (WorldTileCoord sy = 0; sy < DMAXY - sh; sy++) { + for (WorldTileCoord sx = 0; sx < DMAXX - sw; sx++) { if (!miniset.matches({ sx, sy })) continue; // BUGFIX: This should not be applied to Nest levels @@ -1680,8 +1680,8 @@ void Fence() } } - for (int j = 1; j < DMAXY; j++) { // BUGFIX: Change '0' to '1' (fixed) - for (int i = 1; i < DMAXX; i++) { // BUGFIX: Change '0' to '1' (fixed) + for (WorldTileCoord j = 1; j < DMAXY; j++) { // BUGFIX: Change '0' to '1' (fixed) + for (WorldTileCoord i = 1; i < DMAXX; i++) { // BUGFIX: Change '0' to '1' (fixed) // note the comma operator is used here to advance the RNG state if (dungeon[i][j] == 7 && (AdvanceRndSeed(), !IsNearThemeRoom({ i, j }))) { if (FlipCoin()) { @@ -1800,9 +1800,9 @@ void LoadQuestSetPieces() bool PlaceAnvil() { // growing the size by 2 to allow a 1 tile border on all sides - Size areaSize = Size { SDL_SwapLE16(pSetPiece[0]), SDL_SwapLE16(pSetPiece[1]) } + 2; - int sx = GenerateRnd(DMAXX - areaSize.width); - int sy = GenerateRnd(DMAXY - areaSize.height); + WorldTileSize areaSize = WorldTileSize(SDL_SwapLE16(pSetPiece[0]), SDL_SwapLE16(pSetPiece[1])) + 2; + WorldTileCoord sx = GenerateRnd(DMAXX - areaSize.width); + WorldTileCoord sy = GenerateRnd(DMAXY - areaSize.height); for (int trys = 0;; trys++, sx++) { if (trys > 198) @@ -1817,7 +1817,7 @@ bool PlaceAnvil() } bool found = true; - for (Point tile : PointsInRectangleRange { { { sx, sy }, areaSize } }) { + for (WorldTilePosition tile : PointsInRectangle(WorldTileRectangle { { sx, sy }, areaSize })) { if (Protected.test(tile.x, tile.y) || dungeon[tile.x][tile.y] != 7) { found = false; break; @@ -1830,7 +1830,7 @@ bool PlaceAnvil() PlaceDunTiles(pSetPiece.get(), { sx + 1, sy + 1 }, 7); SetPiece = { { sx, sy }, areaSize }; - for (Point tile : PointsInRectangleRange { SetPiece }) { + for (WorldTilePosition tile : PointsInRectangle(SetPiece)) { Protected.set(tile.x, tile.y); } diff --git a/Source/levels/drlg_l4.cpp b/Source/levels/drlg_l4.cpp index 96c56d514..e8184fa5e 100644 --- a/Source/levels/drlg_l4.cpp +++ b/Source/levels/drlg_l4.cpp @@ -14,15 +14,15 @@ namespace devilution { -Point DiabloQuad1; -Point DiabloQuad2; -Point DiabloQuad3; -Point DiabloQuad4; +WorldTilePosition DiabloQuad1; +WorldTilePosition DiabloQuad2; +WorldTilePosition DiabloQuad3; +WorldTilePosition DiabloQuad4; namespace { bool hallok[20]; -Point L4Hold; +WorldTilePosition L4Hold; /** * A lookup table for the 16 possible patterns of a 2x2 area, @@ -172,16 +172,16 @@ void InitDungeonFlags() memset(dungeon, 30, sizeof(dungeon)); } -void MapRoom(Rectangle room) +void MapRoom(WorldTileRectangle room) { - for (int y = 0; y < room.size.height && y + room.position.y < DMAXY / 2; y++) { - for (int x = 0; x < room.size.width && x + room.position.x < DMAXX / 2; x++) { + for (WorldTileCoord y = 0; y < room.size.height && y + room.position.y < DMAXY / 2; y++) { + for (WorldTileCoord x = 0; x < room.size.width && x + room.position.x < DMAXX / 2; x++) { DungeonMask.set(room.position.x + x, room.position.y + y); } } } -bool CheckRoom(Rectangle room) +bool CheckRoom(WorldTileRectangle room) { if (room.position.x <= 0 || room.position.y <= 0) { return false; @@ -201,39 +201,41 @@ bool CheckRoom(Rectangle room) return true; } -void GenerateRoom(Rectangle area, bool verticalLayout) +void GenerateRoom(WorldTileRectangle area, bool verticalLayout) { bool rotate = !FlipCoin(4); verticalLayout = (!verticalLayout && rotate) || (verticalLayout && !rotate); bool placeRoom1; - Rectangle room1; + WorldTileRectangle room1; for (int num = 0; num < 20; num++) { - room1.size = { (GenerateRnd(5) + 2) & ~1, (GenerateRnd(5) + 2) & ~1 }; + const int32_t randomWidth = (GenerateRnd(5) + 2) & ~1; + const int32_t randomHeight = (GenerateRnd(5) + 2) & ~1; + room1.size = WorldTileSize(randomWidth, randomHeight); room1.position = area.position; if (verticalLayout) { - room1.position += Displacement { -room1.size.width, area.size.height / 2 - room1.size.height / 2 }; - placeRoom1 = CheckRoom({ room1.position + Displacement { -1, -1 }, { room1.size.height + 2, room1.size.width + 1 } }); /// BUGFIX: swap height and width ({ room1.size.width + 1, room1.size.height + 2 }) (workaround applied below) + room1.position += WorldTileDisplacement(-room1.size.width, area.size.height / 2 - room1.size.height / 2); + placeRoom1 = CheckRoom({ room1.position + WorldTileDisplacement { -1, -1 }, WorldTileSize(room1.size.height + 2, room1.size.width + 1) }); /// BUGFIX: swap height and width ({ room1.size.width + 1, room1.size.height + 2 }) (workaround applied below) } else { - room1.position += Displacement { area.size.width / 2 - room1.size.width / 2, -room1.size.height }; - placeRoom1 = CheckRoom({ room1.position + Displacement { -1, -1 }, { room1.size.width + 2, room1.size.height + 1 } }); + room1.position += WorldTileDisplacement(area.size.width / 2 - room1.size.width / 2, -room1.size.height); + placeRoom1 = CheckRoom({ room1.position + WorldTileDisplacement { -1, -1 }, WorldTileSize(room1.size.width + 2, room1.size.height + 1) }); } if (placeRoom1) break; } if (placeRoom1) - MapRoom({ room1.position, { std::min(DMAXX - room1.position.x, room1.size.width), std::min(DMAXX - room1.position.y, room1.size.height) } }); + MapRoom({ room1.position, WorldTileSize(std::min(DMAXX - room1.position.x, room1.size.width), std::min(DMAXX - room1.position.y, room1.size.height)) }); bool placeRoom2; - Rectangle room2 = room1; + WorldTileRectangle room2 = room1; if (verticalLayout) { room2.position.x = area.position.x + area.size.width; - placeRoom2 = CheckRoom({ room2.position + Displacement { 0, -1 }, { room2.size.width + 1, room2.size.height + 2 } }); + placeRoom2 = CheckRoom({ room2.position + WorldTileDisplacement { 0, -1 }, WorldTileSize(room2.size.width + 1, room2.size.height + 2) }); } else { room2.position.y = area.position.y + area.size.height; - placeRoom2 = CheckRoom({ room2.position + Displacement { -1, 0 }, { room2.size.width + 2, room2.size.height + 1 } }); + placeRoom2 = CheckRoom({ room2.position + WorldTileDisplacement { -1, 0 }, WorldTileSize(room2.size.width + 2, room2.size.height + 1) }); } if (placeRoom2) @@ -246,7 +248,7 @@ void GenerateRoom(Rectangle area, bool verticalLayout) void FirstRoom() { - Rectangle room { { 0, 0 }, { 14, 14 } }; + WorldTileRectangle room { { 0, 0 }, { 14, 14 } }; if (currlevel != 16) { if (currlevel == Quests[Q_WARLORD]._qlevel && Quests[Q_WARLORD]._qactive != QUEST_NOTAVAIL) { assert(!gbIsMultiplayer); @@ -254,7 +256,9 @@ void FirstRoom() } else if (currlevel == Quests[Q_BETRAYER]._qlevel && gbIsMultiplayer) { room.size = { 11, 11 }; } else { - room.size = { GenerateRnd(5) + 2, GenerateRnd(5) + 2 }; + const int32_t randomWidth = GenerateRnd(5) + 2; + const int32_t randomHeight = GenerateRnd(5) + 2; + room.size = WorldTileSize(randomWidth, randomHeight); } } @@ -262,13 +266,15 @@ void FirstRoom() int xmax = DMAXX / 2 - 1 - room.size.width; int ymin = (DMAXY / 2 - room.size.height) / 2; int ymax = DMAXY / 2 - 1 - room.size.height; - room.position = { GenerateRnd(xmax - xmin + 1) + xmin, GenerateRnd(ymax - ymin + 1) + ymin }; + const int32_t randomX = GenerateRnd(xmax - xmin + 1) + xmin; + const int32_t randomY = GenerateRnd(ymax - ymin + 1) + ymin; + room.position = WorldTilePosition(randomX, randomY); if (currlevel == 16) { L4Hold = room.position; } if (Quests[Q_WARLORD].IsAvailable() || (currlevel == Quests[Q_BETRAYER]._qlevel && gbIsMultiplayer)) { - SetPieceRoom = { room.position + Displacement { 1, 1 }, { room.size.width + 1, room.size.height + 1 } }; + SetPieceRoom = { room.position + WorldTileDisplacement { 1, 1 }, WorldTileSize(room.size.width + 1, room.size.height + 1) }; } else { SetPieceRoom = {}; } @@ -940,22 +946,22 @@ void LoadDiabQuads(bool preflag) { { auto dunData = LoadFileInMem("levels\\l4data\\diab1.dun"); - DiabloQuad1 = L4Hold + Displacement { 4, 4 }; + DiabloQuad1 = L4Hold + WorldTileDisplacement { 4, 4 }; PlaceDunTiles(dunData.get(), DiabloQuad1, 6); } { auto dunData = LoadFileInMem(preflag ? "levels\\l4data\\diab2b.dun" : "levels\\l4data\\diab2a.dun"); - DiabloQuad2 = { 27 - L4Hold.x, 1 + L4Hold.y }; + DiabloQuad2 = WorldTilePosition(27 - L4Hold.x, 1 + L4Hold.y); PlaceDunTiles(dunData.get(), DiabloQuad2, 6); } { auto dunData = LoadFileInMem(preflag ? "levels\\l4data\\diab3b.dun" : "levels\\l4data\\diab3a.dun"); - DiabloQuad3 = { 1 + L4Hold.x, 27 - L4Hold.y }; + DiabloQuad3 = WorldTilePosition(1 + L4Hold.x, 27 - L4Hold.y); PlaceDunTiles(dunData.get(), DiabloQuad3, 6); } { auto dunData = LoadFileInMem(preflag ? "levels\\l4data\\diab4b.dun" : "levels\\l4data\\diab4a.dun"); - DiabloQuad4 = { 28 - L4Hold.x, 28 - L4Hold.y }; + DiabloQuad4 = WorldTilePosition(28 - L4Hold.x, 28 - L4Hold.y); PlaceDunTiles(dunData.get(), DiabloQuad4, 6); } } @@ -1174,10 +1180,10 @@ void GenerateLevel(lvl_entry entry) DRLG_CheckQuests(SetPieceRoom.position); if (currlevel == 15) { - for (int j = 0; j < DMAXY; j++) { - for (int i = 0; i < DMAXX; i++) { + for (WorldTileCoord j = 1; j < DMAXY; j++) { + for (WorldTileCoord i = 1; i < DMAXX; i++) { if (IsAnyOf(dungeon[i][j], 98, 107)) { - Make_SetPC({ { i - 1, j - 1 }, { 5, 5 } }); + Make_SetPC({ WorldTilePosition(i - 1, j - 1), { 5, 5 } }); if (Quests[Q_BETRAYER]._qactive >= QUEST_ACTIVE) { /// Lazarus staff skip bug fixed // Set the portal position to the location of the northmost pentagram tile. Quests[Q_BETRAYER].position = { i, j }; diff --git a/Source/levels/drlg_l4.h b/Source/levels/drlg_l4.h index f14168b4c..5167163d1 100644 --- a/Source/levels/drlg_l4.h +++ b/Source/levels/drlg_l4.h @@ -5,14 +5,15 @@ */ #pragma once +#include "engine/world_tile.hpp" #include "levels/gendung.h" namespace devilution { -extern Point DiabloQuad1; -extern Point DiabloQuad2; -extern Point DiabloQuad3; -extern Point DiabloQuad4; +extern WorldTilePosition DiabloQuad1; +extern WorldTilePosition DiabloQuad2; +extern WorldTilePosition DiabloQuad3; +extern WorldTilePosition DiabloQuad4; void CreateL4Dungeon(uint32_t rseed, lvl_entry entry); void LoadPreL4Dungeon(const char *path); diff --git a/Source/levels/gendung.cpp b/Source/levels/gendung.cpp index d22f6985f..04dbd2a90 100644 --- a/Source/levels/gendung.cpp +++ b/Source/levels/gendung.cpp @@ -19,15 +19,15 @@ Bitset2d DungeonMask; uint8_t dungeon[DMAXX][DMAXY]; uint8_t pdungeon[DMAXX][DMAXY]; Bitset2d Protected; -Rectangle SetPieceRoom; -Rectangle SetPiece; +WorldTileRectangle SetPieceRoom; +WorldTileRectangle SetPiece; std::unique_ptr pSetPiece; OptionalOwnedClxSpriteList pSpecialCels; std::unique_ptr pMegaTiles; std::unique_ptr pDungeonCels; std::array SOLData; -Point dminPosition; -Point dmaxPosition; +WorldTilePosition dminPosition; +WorldTilePosition dmaxPosition; dungeon_type leveltype; uint8_t currlevel; bool setlevel; @@ -89,7 +89,7 @@ std::unique_ptr LoadMinData(size_t &tileCount) * @param maxSize maximum allowable value for both dimensions * @return how much width/height is available for a theme room or an empty optional if there's not enough space */ -std::optional GetSizeForThemeRoom(int floor, Point origin, int minSize, int maxSize) +std::optional GetSizeForThemeRoom(uint8_t floor, WorldTilePosition origin, WorldTileCoord minSize, WorldTileCoord maxSize) { if (origin.x + maxSize > DMAXX && origin.y + maxSize > DMAXY) { return {}; // Original broken bounds check, avoids lower right corner @@ -98,13 +98,13 @@ std::optional GetSizeForThemeRoom(int floor, Point origin, int minSize, in return {}; } - const int maxWidth = std::min(maxSize, DMAXX - origin.x); - const int maxHeight = std::min(maxSize, DMAXY - origin.y); + const WorldTileCoord maxWidth = std::min(maxSize, DMAXX - origin.x); + const WorldTileCoord maxHeight = std::min(maxSize, DMAXY - origin.y); - Size room { maxWidth, maxHeight }; + WorldTileSize room { maxWidth, maxHeight }; - for (int i = 0; i < maxSize; i++) { - int width = i < room.height ? i : 0; + for (WorldTileCoord i = 0; i < maxSize; i++) { + WorldTileCoord width = i < room.height ? i : 0; if (i < maxHeight) { while (width < room.width) { if (dungeon[origin.x + width][origin.y + i] != floor) @@ -114,7 +114,7 @@ std::optional GetSizeForThemeRoom(int floor, Point origin, int minSize, in } } - int height = i < room.width ? i : 0; + WorldTileCoord height = i < room.width ? i : 0; if (i < maxWidth) { while (height < room.height) { if (dungeon[origin.x + i][origin.y + height] != floor) @@ -342,16 +342,16 @@ void InitGlobals() DRLG_InitTrans(); - dminPosition = Point(0, 0).megaToWorld(); - dmaxPosition = Point(40, 40).megaToWorld(); - SetPieceRoom = { { -1, -1 }, { -1, -1 } }; + dminPosition = WorldTilePosition(0, 0).megaToWorld(); + dmaxPosition = WorldTilePosition(40, 40).megaToWorld(); + SetPieceRoom = { { 0, 0 }, { 0, 0 } }; SetPiece = { { 0, 0 }, { 0, 0 } }; } } // namespace #ifdef BUILD_TESTING -std::optional GetSizeForThemeRoom() +std::optional GetSizeForThemeRoom() { return GetSizeForThemeRoom(0, { 0, 0 }, 5, 10); } @@ -500,10 +500,10 @@ void DRLG_InitTrans() TransVal = 1; } -void DRLG_RectTrans(Rectangle area) +void DRLG_RectTrans(WorldTileRectangle area) { - Point position = area.position; - Size size = area.size; + WorldTilePosition position = area.position; + WorldTileSize size = area.size; for (int j = position.y; j <= position.y + size.height; j++) { for (int i = position.x; i <= position.x + size.width; i++) { @@ -514,17 +514,17 @@ void DRLG_RectTrans(Rectangle area) TransVal++; } -void DRLG_MRectTrans(Rectangle area) +void DRLG_MRectTrans(WorldTileRectangle area) { - Point position = area.position.megaToWorld(); - Size size = area.size * 2; + WorldTilePosition position = area.position.megaToWorld(); + WorldTileSize size = area.size * 2; - DRLG_RectTrans({ position + Displacement { 1, 1 }, { size.width - 1, size.height - 1 } }); + DRLG_RectTrans({ position + WorldTileDisplacement { 1, 1 }, WorldTileSize(size.width - 1, size.height - 1) }); } -void DRLG_MRectTrans(Point origin, Point extent) +void DRLG_MRectTrans(WorldTilePosition origin, WorldTilePosition extent) { - DRLG_MRectTrans({ origin, { extent.x - origin.x, extent.y - origin.y } }); + DRLG_MRectTrans({ origin, WorldTileSize(extent.x - origin.x, extent.y - origin.y) }); } void DRLG_CopyTrans(int sx, int sy, int dx, int dy) @@ -569,13 +569,13 @@ void LoadDungeonBase(const char *path, Point spawn, int floorId, int dirtId) SetMapObjects(dunData.get(), 0, 0); } -void Make_SetPC(Rectangle area) +void Make_SetPC(WorldTileRectangle area) { - Point position = area.position.megaToWorld(); - Size size = area.size * 2; + WorldTilePosition position = area.position.megaToWorld(); + WorldTileSize size = area.size * 2; - for (int j = 0; j < size.height; j++) { - for (int i = 0; i < size.width; i++) { + for (unsigned j = 0; j < size.height; j++) { + for (unsigned i = 0; i < size.width; i++) { dFlags[position.x + i][position.y + j] |= DungeonFlag::Populated; } } @@ -649,10 +649,10 @@ void DRLG_PlaceThemeRooms(int minSize, int maxSize, int floor, int freq, bool rn { themeCount = 0; memset(themeLoc, 0, sizeof(*themeLoc)); - for (int j = 0; j < DMAXY; j++) { - for (int i = 0; i < DMAXX; i++) { + for (WorldTileCoord j = 0; j < DMAXY; j++) { + for (WorldTileCoord i = 0; i < DMAXX; i++) { if (dungeon[i][j] == floor && FlipCoin(freq)) { - std::optional themeSize = GetSizeForThemeRoom(floor, { i, j }, minSize, maxSize); + std::optional themeSize = GetSizeForThemeRoom(floor, { i, j }, minSize, maxSize); if (!themeSize) continue; @@ -669,7 +669,7 @@ void DRLG_PlaceThemeRooms(int minSize, int maxSize, int floor, int freq, bool rn } THEME_LOC &theme = themeLoc[themeCount]; - theme.room = { Point { i, j } + Direction::South, *themeSize }; + theme.room = { WorldTilePosition { i, j } + Direction::South, *themeSize }; if (IsAnyOf(leveltype, DTYPE_CAVES, DTYPE_NEST)) { DRLG_RectTrans({ (theme.room.position + Direction::South).megaToWorld(), theme.room.size * 2 - 5 }); } else { @@ -699,13 +699,13 @@ void DRLG_HoldThemeRooms() } } -void SetSetPieceRoom(Point position, int floorId) +void SetSetPieceRoom(WorldTilePosition position, int floorId) { if (pSetPiece == nullptr) return; PlaceDunTiles(pSetPiece.get(), position, floorId); - SetPiece = { position, { SDL_SwapLE16(pSetPiece[0]), SDL_SwapLE16(pSetPiece[1]) } }; + SetPiece = { position, WorldTileSize(SDL_SwapLE16(pSetPiece[0]), SDL_SwapLE16(pSetPiece[1])) }; } void FreeQuestSetPieces() @@ -748,10 +748,10 @@ void DRLG_LPass3(int lv) } } -bool IsNearThemeRoom(Point testPosition) +bool IsNearThemeRoom(WorldTilePosition testPosition) { for (int i = 0; i < themeCount; i++) { - if (Rectangle(themeLoc[i].room.position - Displacement { 2 }, themeLoc[i].room.size + 5).contains(testPosition)) + if (WorldTileRectangle(themeLoc[i].room.position - WorldTileDisplacement { 2 }, themeLoc[i].room.size + 5).contains(testPosition)) return true; } diff --git a/Source/levels/gendung.h b/Source/levels/gendung.h index ad115efda..2a37e478c 100644 --- a/Source/levels/gendung.h +++ b/Source/levels/gendung.h @@ -13,6 +13,7 @@ #include "engine/point.hpp" #include "engine/rectangle.hpp" #include "engine/render/scrollrt.h" +#include "engine/world_tile.hpp" #include "utils/attributes.h" #include "utils/bitset2d.hpp" #include "utils/enum_traits.h" @@ -120,7 +121,7 @@ enum _difficulty : uint8_t { }; struct THEME_LOC { - Rectangle room; + RectangleOf room; int16_t ttval; }; @@ -153,9 +154,9 @@ extern DVL_API_FOR_TEST uint8_t dungeon[DMAXX][DMAXY]; extern uint8_t pdungeon[DMAXX][DMAXY]; /** Tile that may not be overwritten by the level generator */ extern Bitset2d Protected; -extern Rectangle SetPieceRoom; +extern WorldTileRectangle SetPieceRoom; /** Specifies the active set quest piece in coordinate. */ -extern Rectangle SetPiece; +extern WorldTileRectangle SetPiece; /** Contains the contents of the single player quest DUN file. */ extern std::unique_ptr pSetPiece; extern OptionalOwnedClxSpriteList pSpecialCels; @@ -167,9 +168,9 @@ extern std::unique_ptr pDungeonCels; */ extern DVL_API_FOR_TEST std::array SOLData; /** Specifies the minimum X,Y-coordinates of the map. */ -extern Point dminPosition; +extern WorldTilePosition dminPosition; /** Specifies the maximum X,Y-coordinates of the map. */ -extern Point dmaxPosition; +extern WorldTilePosition dmaxPosition; /** Specifies the active dungeon type of the current game. */ extern DVL_API_FOR_TEST dungeon_type leveltype; /** Specifies the active dungeon level of the current game. */ @@ -223,7 +224,7 @@ extern int themeCount; extern THEME_LOC themeLoc[MAXTHEMES]; #ifdef BUILD_TESTING -std::optional GetSizeForThemeRoom(); +std::optional GetSizeForThemeRoom(); #endif dungeon_type GetLevelType(int level); @@ -291,7 +292,7 @@ constexpr bool IsTileLit(Point position) } struct Miniset { - Size size; + WorldTileSize size; /* these are indexed as [y][x] */ uint8_t search[6][6]; uint8_t replace[6][6]; @@ -300,10 +301,10 @@ struct Miniset { * @param position Coordinates of the dungeon tile to check * @param respectProtected Match bug from Crypt levels if false */ - bool matches(Point position, bool respectProtected = true) const + bool matches(WorldTilePosition position, bool respectProtected = true) const { - for (int yy = 0; yy < size.height; yy++) { - for (int xx = 0; xx < size.width; xx++) { + for (WorldTileCoord yy = 0; yy < size.height; yy++) { + for (WorldTileCoord xx = 0; xx < size.width; xx++) { if (search[yy][xx] != 0 && dungeon[xx + position.x][yy + position.y] != search[yy][xx]) return false; if (respectProtected && Protected.test(xx + position.x, yy + position.y)) @@ -313,10 +314,10 @@ struct Miniset { return true; } - void place(Point position, bool protect = false) const + void place(WorldTilePosition position, bool protect = false) const { - for (int y = 0; y < size.height; y++) { - for (int x = 0; x < size.width; x++) { + for (WorldTileCoord y = 0; y < size.height; y++) { + for (WorldTileCoord x = 0; x < size.width; x++) { if (replace[y][x] == 0) continue; dungeon[x + position.x][y + position.y] = replace[y][x]; @@ -331,13 +332,13 @@ bool TileHasAny(int tileId, TileProperties property); void LoadLevelSOLData(); void SetDungeonMicros(); void DRLG_InitTrans(); -void DRLG_MRectTrans(Point origin, Point extent); -void DRLG_MRectTrans(Rectangle area); -void DRLG_RectTrans(Rectangle area); +void DRLG_MRectTrans(WorldTilePosition origin, WorldTilePosition extent); +void DRLG_MRectTrans(WorldTileRectangle area); +void DRLG_RectTrans(WorldTileRectangle area); void DRLG_CopyTrans(int sx, int sy, int dx, int dy); void LoadTransparency(const uint16_t *dunData); void LoadDungeonBase(const char *path, Point spawn, int floorId, int dirtId); -void Make_SetPC(Rectangle area); +void Make_SetPC(WorldTileRectangle area); /** * @param miniset The miniset to place * @param tries Tiles to try, 1600 will scan the full map @@ -347,7 +348,7 @@ std::optional PlaceMiniSet(const Miniset &miniset, int tries = 199, bool void PlaceDunTiles(const uint16_t *dunData, Point position, int floorId = 0); void DRLG_PlaceThemeRooms(int minSize, int maxSize, int floor, int freq, bool rndSize); void DRLG_HoldThemeRooms(); -void SetSetPieceRoom(Point position, int floorId); +void SetSetPieceRoom(WorldTilePosition position, int floorId); void FreeQuestSetPieces(); void DRLG_LPass3(int lv); @@ -356,7 +357,7 @@ void DRLG_LPass3(int lv); * @param position Target location in dungeon coordinates * @return True if a theme room is near (within 2 tiles of) this point, false if it is free. */ -bool IsNearThemeRoom(Point position); +bool IsNearThemeRoom(WorldTilePosition position); void InitLevels(); void FloodTransparencyValues(uint8_t floorID); diff --git a/Source/levels/setmaps.cpp b/Source/levels/setmaps.cpp index f3ab23384..380fe96e4 100644 --- a/Source/levels/setmaps.cpp +++ b/Source/levels/setmaps.cpp @@ -35,13 +35,13 @@ namespace { void AddSKingObjs() { - constexpr Rectangle SmallSecretRoom { { 20, 7 }, { 3, 3 } }; + constexpr WorldTileRectangle SmallSecretRoom { { 20, 7 }, { 3, 3 } }; ObjectAtPosition({ 64, 34 }).InitializeLoadedObject(SmallSecretRoom, 1); - constexpr Rectangle Gate { { 20, 14 }, { 1, 2 } }; + constexpr WorldTileRectangle Gate { { 20, 14 }, { 1, 2 } }; ObjectAtPosition({ 64, 59 }).InitializeLoadedObject(Gate, 2); - constexpr Rectangle LargeSecretRoom { { 8, 1 }, { 7, 10 } }; + constexpr WorldTileRectangle LargeSecretRoom { { 8, 1 }, { 7, 10 } }; ObjectAtPosition({ 27, 37 }).InitializeLoadedObject(LargeSecretRoom, 3); ObjectAtPosition({ 46, 35 }).InitializeLoadedObject(LargeSecretRoom, 3); ObjectAtPosition({ 49, 53 }).InitializeLoadedObject(LargeSecretRoom, 3); diff --git a/Source/levels/themes.cpp b/Source/levels/themes.cpp index 3ab592f12..75b8bffb4 100644 --- a/Source/levels/themes.cpp +++ b/Source/levels/themes.cpp @@ -85,7 +85,7 @@ bool TFit_Shrine(int i) bool CheckThemeObj5(Point origin, int8_t regionId) { - const PointsInRectangleRange searchArea { Rectangle { origin, 2 } }; + const auto searchArea = PointsInRectangle(Rectangle { origin, 2 }); return std::all_of(searchArea.cbegin(), searchArea.cend(), [regionId](Point testPosition) { // note out-of-bounds tiles are not solid, this function relies on the guard in TFit_Obj5 and dungeon border if (IsTileSolid(testPosition)) { @@ -110,7 +110,7 @@ bool TFit_Obj5(int t) } int candidatesFound = 0; - for (Point tile : PointsInRectangleRange { { { 0, 0 }, { MAXDUNX, MAXDUNY } } }) { + for (Point tile : PointsInRectangle(Rectangle { { 0, 0 }, { MAXDUNX, MAXDUNY } })) { if (dTransVal[tile.x][tile.y] == themes[t].ttval && IsTileNotSolid(tile) && CheckThemeObj5(tile, themes[t].ttval)) { // Use themex/y to keep track of the last candidate area found, in case we end up with fewer candidates than the target themex = tile.x; @@ -152,7 +152,7 @@ bool TFit_GoatShrine(int t) bool CheckThemeObj3(Point origin, int8_t regionId, unsigned frequency = 0) { - const PointsInRectangleRange searchArea { Rectangle { origin, 1 } }; + const auto searchArea = PointsInRectangle(Rectangle { origin, 1 }); return std::all_of(searchArea.cbegin(), searchArea.cend(), [regionId, frequency](Point testPosition) { if (!InDungeonBounds(testPosition)) { return false; diff --git a/Source/missiles.cpp b/Source/missiles.cpp index a23b6485c..ae4fd9184 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -1475,10 +1475,7 @@ void AddRuneExplosion(Missile &missile, const AddMissileParameter & /*parameter* missile._midam = dmg; - auto searchArea = PointsInRectangleRangeColMajor { - Rectangle { missile.position.tile, 1 } - }; - for (Point position : searchArea) + for (Point position : PointsInRectangleColMajor(Rectangle { missile.position.tile, 1 })) CheckMissileCol(missile, dmg, dmg, false, position, true); } missile._mlid = AddLight(missile.position.start, 8); diff --git a/Source/monster.cpp b/Source/monster.cpp index 608d99050..08bf273c0 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -1420,7 +1420,7 @@ void MonsterTalk(Monster &monster) ObjChangeMap(SetPiece.position.x, SetPiece.position.y, SetPiece.position.x + (SetPiece.size.width / 2) + 2, SetPiece.position.y + (SetPiece.size.height / 2) - 2); auto tren = TransVal; TransVal = 9; - DRLG_MRectTrans({ SetPiece.position, { SetPiece.size.width / 2 + 4, SetPiece.size.height / 2 } }); + DRLG_MRectTrans({ SetPiece.position, WorldTileSize(SetPiece.size.width / 2 + 4, SetPiece.size.height / 2) }); TransVal = tren; Quests[Q_LTBANNER]._qvar1 = 2; if (Quests[Q_LTBANNER]._qactive == QUEST_INIT) @@ -3606,7 +3606,7 @@ void M_StartStand(Monster &monster, Direction md) void M_ClearSquares(const Monster &monster) { - for (Point searchTile : PointsInRectangleRange { Rectangle { monster.position.old, 1 } }) { + for (Point searchTile : PointsInRectangle(Rectangle { monster.position.old, 1 })) { if (FindMonsterAtPosition(searchTile) == &monster) dMonster[searchTile.x][searchTile.y] = 0; } @@ -4181,7 +4181,7 @@ void SyncMonsterAnim(Monster &monster) void M_FallenFear(Point position) { const Rectangle fearArea = Rectangle { position, 4 }; - for (const Point tile : PointsInRectangleRange { fearArea }) { + for (const Point tile : PointsInRectangle(fearArea)) { if (!InDungeonBounds(tile)) continue; int m = dMonster[tile.x][tile.y]; diff --git a/Source/objects.cpp b/Source/objects.cpp index 875869b9a..2685e9766 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -469,7 +469,7 @@ void AddCandles() * @param affectedArea The map region to be updated when this object is activated by the player. * @param msg The quest text to play when the player activates the book. */ -void AddBookLever(_object_id type, Rectangle affectedArea, _speech_id msg) +void AddBookLever(_object_id type, WorldTileRectangle affectedArea, _speech_id msg) { std::optional position = GetRandomObjectPosition({ 2, 2 }); if (!position) @@ -647,7 +647,7 @@ void AddChestTraps() } } -void LoadMapObjects(const char *path, Point start, Rectangle mapRange = {}, int leveridx = 0) +void LoadMapObjects(const char *path, Point start, WorldTileRectangle mapRange = {}, int leveridx = 0) { LoadingMapObjects = true; ApplyObjectLighting = true; @@ -920,10 +920,10 @@ void AddStoryBooks() void AddHookedBodies(int freq) { - for (int j = 0; j < DMAXY; j++) { - int jj = 16 + j * 2; - for (int i = 0; i < DMAXX; i++) { - int ii = 16 + i * 2; + for (WorldTileCoord j = 0; j < DMAXY; j++) { + WorldTileCoord jj = 16 + j * 2; + for (WorldTileCoord i = 0; i < DMAXX; i++) { + WorldTileCoord ii = 16 + i * 2; if (dungeon[i][j] != 1 && dungeon[i][j] != 2) continue; if (!FlipCoin(freq)) @@ -2000,7 +2000,7 @@ void OperateBookLever(Object &questBook, bool sendmsg) SpawnUnique(UITEM_OPTAMULET, SetPiece.position.megaToWorld() + Displacement { 5, 5 }); auto tren = TransVal; TransVal = 9; - DRLG_MRectTrans({ questBook._oVar1, questBook._oVar2 }, { questBook._oVar3, questBook._oVar4 }); + DRLG_MRectTrans(WorldTilePosition(questBook._oVar1, questBook._oVar2), WorldTilePosition(questBook._oVar3, questBook._oVar4)); TransVal = tren; } } @@ -3528,7 +3528,7 @@ void SyncQSTLever(const Object &qstLever) if (qstLever._otype == OBJ_BLINDBOOK) { auto tren = TransVal; TransVal = 9; - DRLG_MRectTrans({ qstLever._oVar1, qstLever._oVar2 }, { qstLever._oVar3, qstLever._oVar4 }); + DRLG_MRectTrans(WorldTilePosition(qstLever._oVar1, qstLever._oVar2), WorldTilePosition(qstLever._oVar3, qstLever._oVar4)); TransVal = tren; } } @@ -3996,7 +3996,7 @@ Object *AddObject(_object_id objType, Point objPos) AddDoor(object); break; case OBJ_BOOK2R: - object.InitializeBook({ SetPiece.position, { SetPiece.size.width + 1, SetPiece.size.height + 1 } }); + object.InitializeBook({ SetPiece.position, WorldTileSize(SetPiece.size.width + 1, SetPiece.size.height + 1) }); break; case OBJ_CHEST1: case OBJ_CHEST2: @@ -4146,7 +4146,7 @@ void OperateTrap(Object &trap) Point triggerPosition = { trap._oVar1, trap._oVar2 }; Point target = triggerPosition; - PointsInRectangleRange searchArea { Rectangle { target, 1 } }; + auto searchArea = PointsInRectangle(Rectangle { target, 1 }); // look for a player near the trigger (using a reverse search to match vanilla behaviour) auto foundPosition = std::find_if(searchArea.crbegin(), searchArea.crend(), [](Point testPosition) { return InDungeonBounds(testPosition) && dPlayer[testPosition.x][testPosition.y] != 0; }); if (foundPosition != searchArea.crend()) { diff --git a/Source/objects.h b/Source/objects.h index 26b610201..f144efde3 100644 --- a/Source/objects.h +++ b/Source/objects.h @@ -10,6 +10,7 @@ #include "engine/clx_sprite.hpp" #include "engine/point.hpp" #include "engine/rectangle.hpp" +#include "engine/world_tile.hpp" #include "itemdat.h" #include "monster.h" #include "objdat.h" @@ -84,7 +85,7 @@ struct Object { * @param topLeftPosition corner of the map region closest to the origin. * @param bottomRightPosition corner of the map region furthest from the origin. */ - constexpr void SetMapRange(Point topLeftPosition, Point bottomRightPosition) + constexpr void SetMapRange(WorldTilePosition topLeftPosition, WorldTilePosition bottomRightPosition) { _oVar1 = topLeftPosition.x; _oVar2 = topLeftPosition.y; @@ -96,9 +97,9 @@ struct Object { * @brief Convenience function for SetMapRange(Point, Point). * @param mapRange A rectangle defining the top left corner and size of the affected region. */ - constexpr void SetMapRange(Rectangle mapRange) + constexpr void SetMapRange(WorldTileRectangle mapRange) { - SetMapRange(mapRange.position, mapRange.position + Displacement { mapRange.size }); + SetMapRange(mapRange.position, mapRange.position + DisplacementOf(mapRange.size)); } /** @@ -109,7 +110,7 @@ struct Object { * * @param mapRange The region to be updated when this object is activated. */ - constexpr void InitializeBook(Rectangle mapRange) + constexpr void InitializeBook(WorldTileRectangle mapRange) { SetMapRange(mapRange); _oVar6 = _oAnimFrame + 1; // Save the frame number for the open book frame @@ -121,7 +122,7 @@ struct Object { * @param leverID An ID (distinct from the object index) to identify the new objects spawned after updating the map. * @param message The quest text to play when this object is activated. */ - constexpr void InitializeQuestBook(Rectangle mapRange, int leverID, _speech_id message) + constexpr void InitializeQuestBook(WorldTileRectangle mapRange, int leverID, _speech_id message) { InitializeBook(mapRange); _oVar8 = leverID; @@ -133,7 +134,7 @@ struct Object { * @param mapRange The region which was updated to spawn this object. * @param leverID The id (*not* an object ID/index) of the lever responsible for the map change. */ - constexpr void InitializeLoadedObject(Rectangle mapRange, int leverID) + constexpr void InitializeLoadedObject(WorldTileRectangle mapRange, int leverID) { SetMapRange(mapRange); _oVar8 = leverID; diff --git a/Source/player.cpp b/Source/player.cpp index 638ba064b..0f864d105 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -2885,7 +2885,7 @@ void StartPlrBlock(Player &player, Direction dir) void FixPlrWalkTags(const Player &player) { - for (Point searchTile : PointsInRectangleRange { Rectangle { player.position.old, 1 } }) { + for (Point searchTile : PointsInRectangle(Rectangle { player.position.old, 1 })) { if (PlayerAtPosition(searchTile) == &player) { dPlayer[searchTile.x][searchTile.y] = 0; } diff --git a/Source/qol/stash.cpp b/Source/qol/stash.cpp index e689b19a0..1e17a09a0 100644 --- a/Source/qol/stash.cpp +++ b/Source/qol/stash.cpp @@ -50,7 +50,7 @@ constexpr Rectangle StashButtonRect[] = { // clang-format on }; -constexpr PointsInRectangleRange StashGridRange { { { 0, 0 }, Size { 10, 10 } } }; +constexpr PointsInRectangleRange StashGridRange { { { 0, 0 }, Size { 10, 10 } } }; OptionalOwnedClxSpriteList StashPanelArt; OptionalOwnedClxSpriteList StashNavButtonArt; @@ -63,7 +63,7 @@ OptionalOwnedClxSpriteList StashNavButtonArt; */ void AddItemToStashGrid(unsigned page, Point position, uint16_t stashListIndex, Size itemSize) { - for (auto point : PointsInRectangleRange({ position, itemSize })) { + for (Point point : PointsInRectangle(Rectangle { position, itemSize })) { Stash.stashGrids[page][point.x][point.y] = stashListIndex + 1; } } @@ -121,7 +121,7 @@ void CheckStashPaste(Point cursorPosition) // Check that no more than 1 item is replaced by the move StashStruct::StashCell stashIndex = StashStruct::EmptyCell; - for (auto point : PointsInRectangleRange({ firstSlot, itemSize })) { + for (Point point : PointsInRectangle(Rectangle { firstSlot, itemSize })) { StashStruct::StashCell iv = Stash.GetItemIdAtPosition(point); if (iv == StashStruct::EmptyCell || stashIndex == iv) continue; @@ -681,10 +681,10 @@ bool AutoPlaceItemInStash(Player &player, const Item &item, bool persistItem) if (pageIndex >= CountStashPages) pageIndex -= CountStashPages; // Search all possible position in stash grid - for (auto stashPosition : PointsInRectangleRange({ { 0, 0 }, Size { 10 - (itemSize.width - 1), 10 - (itemSize.height - 1) } })) { + for (auto stashPosition : PointsInRectangle(Rectangle { { 0, 0 }, Size { 10 - (itemSize.width - 1), 10 - (itemSize.height - 1) } })) { // Check that all needed slots are free bool isSpaceFree = true; - for (auto itemPoint : PointsInRectangleRange({ stashPosition, itemSize })) { + for (auto itemPoint : PointsInRectangle(Rectangle { stashPosition, itemSize })) { uint16_t iv = Stash.stashGrids[pageIndex][itemPoint.x][itemPoint.y]; if (iv != 0) { isSpaceFree = false; diff --git a/Source/quests.cpp b/Source/quests.cpp index cf5566b0f..8e79d1fc2 100644 --- a/Source/quests.cpp +++ b/Source/quests.cpp @@ -14,6 +14,7 @@ #include "engine/random.hpp" #include "engine/render/clx_render.hpp" #include "engine/render/text_render.hpp" +#include "engine/world_tile.hpp" #include "init.h" #include "levels/gendung.h" #include "levels/trigs.h" @@ -139,7 +140,7 @@ void DrawWarLord(Point position) { auto dunData = LoadFileInMem("levels\\l4data\\warlord2.dun"); - SetPiece = { position, { SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1]) } }; + SetPiece = { position, WorldTileSize(SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1])) }; PlaceDunTiles(dunData.get(), position, 6); } @@ -148,7 +149,7 @@ void DrawSChamber(quest_id q, Point position) { auto dunData = LoadFileInMem("levels\\l2data\\bonestr1.dun"); - SetPiece = { position, { SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1]) } }; + SetPiece = { position, WorldTileSize(SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1])) }; PlaceDunTiles(dunData.get(), position, 3); @@ -162,7 +163,7 @@ void DrawLTBanner(Point position) int width = SDL_SwapLE16(dunData[0]); int height = SDL_SwapLE16(dunData[1]); - SetPiece = { position, { SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1]) } }; + SetPiece = { position, WorldTileSize(SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1])) }; const uint16_t *tileLayer = &dunData[2]; @@ -189,7 +190,7 @@ void DrawBlood(Point position) { auto dunData = LoadFileInMem("levels\\l2data\\blood2.dun"); - SetPiece = { position, { SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1]) } }; + SetPiece = { position, WorldTileSize(SDL_SwapLE16(dunData[0]), SDL_SwapLE16(dunData[1])) }; PlaceDunTiles(dunData.get(), position, 0); } @@ -626,7 +627,7 @@ void ResyncQuests() SyncObjectAnim(Objects[ActiveObjects[i]]); auto tren = TransVal; TransVal = 9; - DRLG_MRectTrans({ SetPiece.position, { SetPiece.size.width / 2 + 4, SetPiece.size.height / 2 } }); + DRLG_MRectTrans({ SetPiece.position, WorldTileSize(SetPiece.size.width / 2 + 4, SetPiece.size.height / 2) }); TransVal = tren; } if (Quests[Q_LTBANNER]._qvar1 == 3) { @@ -635,7 +636,7 @@ void ResyncQuests() SyncObjectAnim(Objects[ActiveObjects[i]]); auto tren = TransVal; TransVal = 9; - DRLG_MRectTrans({ SetPiece.position, { SetPiece.size.width / 2 + 4, SetPiece.size.height / 2 } }); + DRLG_MRectTrans({ SetPiece.position, WorldTileSize(SetPiece.size.width / 2 + 4, SetPiece.size.height / 2) }); TransVal = tren; } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f03650ff4..566882956 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -34,6 +34,7 @@ set(tests player_test quests_test random_test + rectangle_test scrollrt_test stores_test timedemo_test diff --git a/test/drlg_common_test.cpp b/test/drlg_common_test.cpp index 2c5de68e8..399167e27 100644 --- a/test/drlg_common_test.cpp +++ b/test/drlg_common_test.cpp @@ -4,20 +4,21 @@ #include #include "engine/points_in_rectangle_range.hpp" +#include "engine/world_tile.hpp" #include "levels/gendung.h" namespace devilution { TEST(DrlgTest, RectangleRangeIterator) { - constexpr Rectangle topLeftArea = Rectangle { Point { 1, 1 }, 1 }; - constexpr Rectangle bottomRightArea = Rectangle { Point { 3, 3 }, 1 }; + constexpr WorldTileRectangle topLeftArea { { 1, 1 }, 1 }; + constexpr WorldTileRectangle bottomRightArea { { 3, 3 }, 1 }; // Dungeon generation depends on the iteration order remaining unchanged to ensure we generate the same layout as vanilla Diablo/Hellfire std::array, 5> region {}; int counter = 0; // Iterate over a 9 tile area in the top left of the region. - for (Point position : PointsInRectangleRange { topLeftArea }) { + for (WorldTilePosition position : PointsInRectangle(topLeftArea)) { region[position.x][position.y] = ++counter; } @@ -35,7 +36,7 @@ TEST(DrlgTest, RectangleRangeIterator) region = {}; counter = 0; - PointsInRectangleRange rowMajorRange { bottomRightArea }; + const auto rowMajorRange = PointsInRectangle(bottomRightArea); std::for_each(rowMajorRange.rbegin(), rowMajorRange.rend(), [®ion, &counter](Point position) { region[position.x][position.y] = ++counter; }); EXPECT_EQ(region[4][4], 1) << "Reverse iterators are required"; EXPECT_EQ(region[2][4], 3) << "Reverse iterators are required"; @@ -45,7 +46,7 @@ TEST(DrlgTest, RectangleRangeIterator) region = {}; counter = 0; - for (Point position : PointsInRectangleRangeColMajor { topLeftArea }) { + for (WorldTilePosition position : PointsInRectangleColMajor(topLeftArea)) { region[position.x][position.y] = ++counter; } @@ -63,7 +64,7 @@ TEST(DrlgTest, RectangleRangeIterator) region = {}; counter = 0; - PointsInRectangleRangeColMajor colMajorRange { bottomRightArea }; + const auto colMajorRange = PointsInRectangleColMajor(bottomRightArea); std::for_each(colMajorRange.rbegin(), colMajorRange.rend(), [®ion, &counter](Point position) { region[position.x][position.y] = ++counter; }); EXPECT_EQ(region[4][4], 1) << "Reverse iterators are required"; EXPECT_EQ(region[4][2], 3) << "Reverse iterators are required"; @@ -75,14 +76,14 @@ TEST(DrlgTest, ThemeRoomSize) { memset(dungeon, 0, sizeof(dungeon)); - EXPECT_EQ(GetSizeForThemeRoom(), Size(8, 8)) << "All floor theme area should be 8x8"; + EXPECT_EQ(GetSizeForThemeRoom(), WorldTileSize(8, 8)) << "All floor theme area should be 8x8"; dungeon[9][9] = 1; - EXPECT_EQ(GetSizeForThemeRoom(), Size(7, 7)) << "Corners shrink the chosen dimensions"; + EXPECT_EQ(GetSizeForThemeRoom(), WorldTileSize(7, 7)) << "Corners shrink the chosen dimensions"; dungeon[9][5] = 1; - EXPECT_EQ(GetSizeForThemeRoom(), Size(7, 3)) << "Minimum dimensions are determined by corners outside the min area"; + EXPECT_EQ(GetSizeForThemeRoom(), WorldTileSize(7, 3)) << "Minimum dimensions are determined by corners outside the min area"; dungeon[9][4] = 1; - EXPECT_EQ(GetSizeForThemeRoom(), Size(7, 8)) << "Walls below the min size let larger opposing dimensions get picked"; + EXPECT_EQ(GetSizeForThemeRoom(), WorldTileSize(7, 8)) << "Walls below the min size let larger opposing dimensions get picked"; dungeon[9][5] = 0; dungeon[9][4] = 0; dungeon[9][9] = 0; @@ -90,10 +91,10 @@ TEST(DrlgTest, ThemeRoomSize) // Time for some unusual cases dungeon[7][2] = 1; dungeon[5][9] = 1; - EXPECT_EQ(GetSizeForThemeRoom(), Size(5, 7)) << "Search space terminates at width 8 due to the wall being in the first three rows"; + EXPECT_EQ(GetSizeForThemeRoom(), WorldTileSize(5, 7)) << "Search space terminates at width 8 due to the wall being in the first three rows"; dungeon[6][4] = 1; - EXPECT_EQ(GetSizeForThemeRoom(), Size(4, 7)) << "Smallest width now defined by row 5, height still extends due to minSize"; + EXPECT_EQ(GetSizeForThemeRoom(), WorldTileSize(4, 7)) << "Smallest width now defined by row 5, height still extends due to minSize"; dungeon[6][4] = 0; dungeon[5][9] = 0; @@ -102,7 +103,7 @@ TEST(DrlgTest, ThemeRoomSize) dungeon[7][0] = 1; dungeon[6][6] = 1; dungeon[8][5] = 1; - EXPECT_EQ(GetSizeForThemeRoom(), Size(4, 4)) << "Search is terminated by the 0 width row 7, inset corner gives a larger height than otherwise expected"; + EXPECT_EQ(GetSizeForThemeRoom(), WorldTileSize(4, 4)) << "Search is terminated by the 0 width row 7, inset corner gives a larger height than otherwise expected"; } } // namespace devilution diff --git a/test/rectangle_test.cpp b/test/rectangle_test.cpp new file mode 100644 index 000000000..ea3b28efb --- /dev/null +++ b/test/rectangle_test.cpp @@ -0,0 +1,33 @@ +#include + +#include "engine/point.hpp" +#include "engine/rectangle.hpp" + +namespace devilution { +namespace { + +TEST(RectangleTest, Contains_LargerSize) +{ + RectangleOf rect { { 0, 0 }, { 10, 20 } }; + EXPECT_TRUE(rect.contains(Point(9, 9))); + EXPECT_FALSE(rect.contains(Point(-1, -1))); + EXPECT_FALSE(rect.contains(Point(257, 257))); +} + +TEST(RectangleTest, Contains_UnsignedRectangle_SignedPointSameSize) +{ + RectangleOf rect { { 0, 0 }, { 255, 255 } }; + EXPECT_TRUE(rect.contains(PointOf(5, 5))); + EXPECT_FALSE(rect.contains(PointOf(-1, -1))); + EXPECT_FALSE(rect.contains(PointOf(-2, -2))); +} + +TEST(RectangleTest, Contains_SignedRectangle_UnsignedPointSameSize) +{ + RectangleOf rect { { -10, -10 }, { 127, 127 } }; + EXPECT_TRUE(rect.contains(PointOf(0, 0))); + EXPECT_FALSE(rect.contains(PointOf(255, 255))); +} + +} // namespace +} // namespace devilution