From 3b2e560fa0da56c70ae657540980eccacf851cf6 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 2 Jul 2022 00:49:17 +0100 Subject: [PATCH] Non-int versions of `Point` and `Displacement` (#4824) * Non-int versions of `Point` and `Displacement` This will allow us to make some structs, such as `ActorPosition`, much smaller. * ActorPosition: Use smaller types `Monsters`: 56K -> 46K * player.cpp: Reduce size of `DirectionSettings` * CrawlTable: Displacement -> DisplacementOf * CrawlTable: vector -> array Also only allocate one vector during construction instead of two. A bit less indirection. * Monster#enemyPosition: Point -> WorldTilePosition sizeof(Monster): 240 -> 232 * Monster: Further optimize field layout and sizes sizeof(Monster): 232 -> 208 `Monsters` is down to 40,000 bytes * DMonsterStr: _mx/_my -> position --- Source/engine/actor_position.hpp | 19 +-- Source/engine/displacement.hpp | 266 ++++++++++++++++++------------- Source/engine/point.hpp | 180 ++++++++++++--------- Source/engine/world_tile.hpp | 10 ++ Source/lighting.cpp | 44 ++--- Source/lighting.h | 3 +- Source/missiles.cpp | 6 +- Source/monster.cpp | 33 ++-- Source/monster.h | 63 ++++---- Source/msg.cpp | 33 ++-- Source/multi.cpp | 12 +- Source/player.cpp | 30 ++-- Source/player.h | 8 +- Source/utils/static_vector.hpp | 26 +++ 14 files changed, 422 insertions(+), 311 deletions(-) create mode 100644 Source/engine/world_tile.hpp diff --git a/Source/engine/actor_position.hpp b/Source/engine/actor_position.hpp index 363d5e2f3..9afaff65d 100644 --- a/Source/engine/actor_position.hpp +++ b/Source/engine/actor_position.hpp @@ -1,25 +1,26 @@ #pragma once #include "engine/point.hpp" +#include "engine/world_tile.hpp" namespace devilution { struct ActorPosition { - Point tile; + WorldTilePosition tile; /** Future tile position. Set at start of walking animation. */ - Point future; + WorldTilePosition future; /** Tile position of player. Set via network on player input. */ - Point last; + WorldTilePosition last; /** Most recent position in dPlayer. */ - Point old; + WorldTilePosition old; + /** Used for referring to position of player when finishing moving one tile (also used to define target coordinates for spells and ranged attacks) */ + WorldTilePosition temp; /** Pixel offset from tile. */ - Displacement offset; + DisplacementOf offset; /** Same as offset but contains the value in a higher range */ - Displacement offset2; + DisplacementOf offset2; /** Pixel velocity while walking. Indirectly applied to offset via _pvar6/7 */ - Displacement velocity; - /** Used for referring to position of player when finishing moving one tile (also used to define target coordinates for spells and ranged attacks) */ - Point temp; + DisplacementOf velocity; }; } // namespace devilution diff --git a/Source/engine/displacement.hpp b/Source/engine/displacement.hpp index 89bc93be7..4ebf8da00 100644 --- a/Source/engine/displacement.hpp +++ b/Source/engine/displacement.hpp @@ -11,143 +11,104 @@ namespace devilution { -struct Displacement { - int deltaX; - int deltaY; +template +struct DisplacementOf; - Displacement() = default; +using Displacement = DisplacementOf; - constexpr Displacement(int deltaX, int deltaY) +template +struct DisplacementOf { + DeltaT deltaX; + DeltaT deltaY; + + DisplacementOf() = default; + + template + constexpr DisplacementOf(DisplacementOf other) + : deltaX(other.deltaX) + , deltaY(other.deltaY) + { + } + + constexpr DisplacementOf(DeltaT deltaX, DeltaT deltaY) : deltaX(deltaX) , deltaY(deltaY) { } - explicit constexpr Displacement(int delta) + explicit constexpr DisplacementOf(DeltaT delta) : deltaX(delta) , deltaY(delta) { } - explicit constexpr Displacement(const Size &size) + explicit constexpr DisplacementOf(const Size &size) : deltaX(size.width) , deltaY(size.height) { } - explicit constexpr Displacement(Direction direction) - : Displacement(fromDirection(direction)) + explicit constexpr DisplacementOf(Direction direction) + : DisplacementOf(fromDirection(direction)) { } - constexpr bool operator==(const Displacement &other) const + template + constexpr bool operator==(const DisplacementOf &other) const { return deltaX == other.deltaX && deltaY == other.deltaY; } - constexpr bool operator!=(const Displacement &other) const + template + constexpr bool operator!=(const DisplacementOf &other) const { return !(*this == other); } - constexpr Displacement &operator+=(const Displacement &displacement) + template + constexpr DisplacementOf &operator+=(DisplacementOf displacement) { deltaX += displacement.deltaX; deltaY += displacement.deltaY; return *this; } - constexpr Displacement &operator-=(const Displacement &displacement) + template + constexpr DisplacementOf &operator-=(DisplacementOf displacement) { deltaX -= displacement.deltaX; deltaY -= displacement.deltaY; return *this; } - constexpr Displacement &operator*=(const int factor) + constexpr DisplacementOf &operator*=(const int factor) { deltaX *= factor; deltaY *= factor; return *this; } - constexpr Displacement &operator*=(const float factor) + constexpr DisplacementOf &operator*=(const float factor) { - deltaX = static_cast(deltaX * factor); - deltaY = static_cast(deltaY * factor); + deltaX = static_cast(deltaX * factor); + deltaY = static_cast(deltaY * factor); return *this; } - constexpr Displacement &operator/=(const int factor) + constexpr DisplacementOf &operator/=(const int factor) { deltaX /= factor; deltaY /= factor; return *this; } - constexpr Displacement &operator/=(const float factor) + constexpr DisplacementOf &operator/=(const float factor) { - deltaX = static_cast(deltaX / factor); - deltaY = static_cast(deltaY / factor); + deltaX = static_cast(deltaX / factor); + deltaY = static_cast(deltaY / factor); return *this; } - constexpr friend Displacement operator+(Displacement a, Displacement b) - { - a += b; - return a; - } - - constexpr friend Displacement operator-(Displacement a, Displacement b) - { - a -= b; - return a; - } - - constexpr friend Displacement operator*(Displacement a, const int factor) - { - a *= factor; - return a; - } - - constexpr friend Displacement operator*(Displacement a, const float factor) - { - a *= factor; - return a; - } - - constexpr friend Displacement operator/(Displacement a, const int factor) - { - a /= factor; - return a; - } - - constexpr friend Displacement operator/(Displacement a, const float factor) - { - a /= factor; - return a; - } - - constexpr friend Displacement operator-(const Displacement &a) - { - return { -a.deltaX, -a.deltaY }; - } - - constexpr friend Displacement operator<<(Displacement a, unsigned factor) - { - return { a.deltaX << factor, a.deltaY << factor }; - } - - constexpr friend Displacement operator>>(Displacement a, unsigned factor) - { - return { a.deltaX >> factor, a.deltaY >> factor }; - } - - constexpr friend Displacement abs(Displacement a) - { - return { abs(a.deltaX), abs(a.deltaY) }; - } - float magnitude() const { return static_cast(hypot(deltaX, deltaY)); @@ -164,7 +125,7 @@ struct Displacement { * * @return A representation of the original displacement in screen coordinates. */ - constexpr Displacement worldToScreen() const + constexpr DisplacementOf worldToScreen() const { return { (deltaY - deltaX) * 32, (deltaY + deltaX) * -16 }; } @@ -176,7 +137,7 @@ struct Displacement { * * @return A representation of the original displacement in world coordinates. */ - constexpr Displacement screenToWorld() const + constexpr DisplacementOf screenToWorld() const { return { (2 * deltaY + deltaX) / -64, (2 * deltaY - deltaX) / -64 }; } @@ -185,12 +146,12 @@ struct Displacement { * @brief Missiles flip the axes for some reason -_- * @return negated world displacement, for use with missile movement routines. */ - constexpr Displacement screenToMissile() const + constexpr DisplacementOf screenToMissile() const { return -screenToWorld(); } - constexpr Displacement screenToLight() const + constexpr DisplacementOf screenToLight() const { return { (2 * deltaY + deltaX) / 8, (2 * deltaY - deltaX) / 8 }; } @@ -200,14 +161,14 @@ struct Displacement { * * This will return a displacement of the form (-1.0 to 1.0, -0.5 to 0.5), to get a full tile offset you can multiply by 16 */ - Displacement worldToNormalScreen() const + [[nodiscard]] Displacement worldToNormalScreen() const { // Most transformations between world and screen space take shortcuts when scaling to simplify the math. This // routine is typically used with missiles where we want a normal vector that can be multiplied with a target // velocity (given in pixels). We could normalize the vector first but then we'd need to scale it during // rotation from world to screen space. To save performing unnecessary divisions we rotate first without // correcting the scaling. This gives a vector in elevation projection aligned with screen space. - Displacement rotated { (deltaY - deltaX), -(deltaY + deltaX) }; + Displacement rotated { static_cast(deltaY) - static_cast(deltaX), -(static_cast(deltaY) + static_cast(deltaX)) }; // then normalize this vector Displacement rotatedAndNormalized = rotated.normalized(); // and finally scale the y axis to bring it to isometric projection @@ -217,57 +178,43 @@ struct Displacement { /** * @brief Calculates a 16 bit fixed point normalized displacement (having magnitude of ~1.0) from the current Displacement */ - Displacement normalized() const - { - float magnitude = this->magnitude(); - Displacement normalDisplacement = *this << 16; - normalDisplacement /= magnitude; - return normalDisplacement; - } + [[nodiscard]] Displacement normalized() const; - constexpr Displacement Rotate(int quadrants) + [[nodiscard]] constexpr DisplacementOf Rotate(int quadrants) const { - constexpr int Sines[] = { 0, 1, 0, -1 }; + static_assert(std::is_signed::value, "DeltaT must be signed for Rotate"); + constexpr DeltaT Sines[] = { 0, 1, 0, -1 }; quadrants = (quadrants % 4 + 4) % 4; - int sine = Sines[quadrants]; - int cosine = Sines[(quadrants + 1) % 4]; - - return Displacement { deltaX * cosine - deltaY * sine, deltaX * sine + deltaY * cosine }; - } + DeltaT sine = Sines[quadrants]; + DeltaT cosine = Sines[(quadrants + 1) % 4]; - constexpr Displacement flipX() - { - return { -deltaX, deltaY }; + return DisplacementOf { deltaX * cosine - deltaY * sine, deltaX * sine + deltaY * cosine }; } - constexpr Displacement flipY() + [[nodiscard]] constexpr DisplacementOf flipX() const { - return { deltaX, -deltaY }; + static_assert(std::is_signed::value, "DeltaT must be signed for flipX"); + return { static_cast(-deltaX), deltaY }; } - constexpr Displacement flipXY() + [[nodiscard]] constexpr DisplacementOf flipY() const { - return { -deltaX, -deltaY }; + static_assert(std::is_signed::value, "DeltaT must be signed for flipY"); + return { deltaX, static_cast(-deltaY) }; } -#ifdef BUILD_TESTING - /** - * @brief Format displacements nicely in test failure messages - * @param stream output stream, expected to have overloads for int and char* - * @param offset Object to display - * @return the stream, to allow chaining - */ - friend std::ostream &operator<<(std::ostream &stream, const Displacement &offset) + [[nodiscard]] constexpr DisplacementOf flipXY() const { - return stream << "(x: " << offset.deltaX << ", y: " << offset.deltaY << ")"; + static_assert(std::is_signed::value, "DeltaT must be signed for flipXY"); + return { static_cast(-deltaX), static_cast(-deltaY) }; } -#endif private: - static constexpr Displacement fromDirection(Direction direction) + static constexpr DisplacementOf fromDirection(Direction direction) { + static_assert(std::is_signed::value, "DeltaT must be signed for conversion from Direction"); switch (direction) { case Direction::South: return { 1, 1 }; @@ -291,4 +238,93 @@ private: }; }; +#ifdef BUILD_TESTING +/** + * @brief Format displacements nicely in test failure messages + * @param stream output stream, expected to have overloads for int and char* + * @param offset Object to display + * @return the stream, to allow chaining + */ +template +std::ostream &operator<<(std::ostream &stream, const DisplacementOf &offset) +{ + return stream << "(x: " << offset.deltaX << ", y: " << offset.deltaY << ")"; +} +#endif + +template +constexpr DisplacementOf operator+(DisplacementOf a, DisplacementOf b) +{ + a += b; + return a; +} + +template +constexpr DisplacementOf operator-(DisplacementOf a, DisplacementOf b) +{ + a -= b; + return a; +} + +template +constexpr DisplacementOf operator*(DisplacementOf a, const int factor) +{ + a *= factor; + return a; +} + +template +constexpr DisplacementOf operator*(DisplacementOf a, const float factor) +{ + a *= factor; + return a; +} + +template +constexpr DisplacementOf operator/(DisplacementOf a, const int factor) +{ + a /= factor; + return a; +} + +template +constexpr DisplacementOf operator/(DisplacementOf a, const float factor) +{ + a /= factor; + return a; +} + +template +constexpr DisplacementOf operator-(DisplacementOf a) +{ + return { -a.deltaX, -a.deltaY }; +} + +template +constexpr DisplacementOf operator<<(DisplacementOf a, unsigned factor) +{ + return { a.deltaX << factor, a.deltaY << factor }; +} + +template +constexpr DisplacementOf operator>>(DisplacementOf a, unsigned factor) +{ + return { a.deltaX >> factor, a.deltaY >> factor }; +} + +template +constexpr DisplacementOf abs(DisplacementOf a) +{ + return { abs(a.deltaX), abs(a.deltaY) }; +} + +template +Displacement DisplacementOf::normalized() const +{ + const float magnitude = this->magnitude(); + Displacement normalDisplacement = Displacement(*this) << 16u; + normalDisplacement /= magnitude; + return normalDisplacement; +} + } // namespace devilution diff --git a/Source/engine/point.hpp b/Source/engine/point.hpp index b6b5a541f..afd4b0f9c 100644 --- a/Source/engine/point.hpp +++ b/Source/engine/point.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #ifdef BUILD_TESTING #include #endif @@ -12,99 +13,81 @@ namespace devilution { -struct Point { - int x; - int y; +template +struct PointOf; - Point() = default; +using Point = PointOf; - constexpr Point(int x, int y) +template +struct PointOf { + CoordT x; + CoordT y; + + PointOf() = default; + + template + constexpr PointOf(PointOf other) + : x(other.x) + , y(other.y) + { + } + + constexpr PointOf(CoordT x, CoordT y) : x(x) , y(y) { } - constexpr bool operator==(const Point &other) const + template + constexpr bool operator==(const PointOf &other) const { return x == other.x && y == other.y; } - constexpr bool operator!=(const Point &other) const + template + constexpr bool operator!=(const PointOf &other) const { return !(*this == other); } - constexpr Point &operator+=(const Displacement &displacement) + template + constexpr PointOf &operator+=(const DisplacementOf &displacement) { x += displacement.deltaX; y += displacement.deltaY; return *this; } - constexpr Point &operator+=(Direction direction) + constexpr PointOf &operator+=(Direction direction) { - return (*this) += Displacement(direction); + return (*this) += DisplacementOf::type>(direction); } - constexpr Point &operator-=(const Displacement &displacement) + template + constexpr PointOf &operator-=(const DisplacementOf &displacement) { x -= displacement.deltaX; y -= displacement.deltaY; return *this; } - constexpr Point &operator*=(const float factor) + constexpr PointOf &operator*=(const float factor) { x = static_cast(x * factor); y = static_cast(y * factor); return *this; } - constexpr Point &operator*=(const int factor) + constexpr PointOf &operator*=(const int factor) { x *= factor; y *= factor; return *this; } - constexpr friend Point operator+(Point a, Displacement displacement) - { - a += displacement; - return a; - } - - constexpr friend Point operator+(Point a, Direction direction) - { - a += direction; - return a; - } - - constexpr friend Displacement operator-(Point a, const Point &b) - { - return { a.x - b.x, a.y - b.y }; - } - - constexpr friend Point operator-(const Point &a) - { - return { -a.x, -a.y }; - } - - constexpr friend Point operator-(Point a, Displacement displacement) + constexpr PointOf operator-() const { - a -= displacement; - return a; - } - - constexpr friend Point operator*(Point a, const float factor) - { - a *= factor; - return a; - } - - constexpr friend Point operator*(Point a, const int factor) - { - a *= factor; - return a; + return { -x, -y }; } /** @@ -113,9 +96,10 @@ struct Point { * @return Magnitude of vector this -> other */ - constexpr int ApproxDistance(Point other) const + template + constexpr int ApproxDistance(PointOf other) const { - Displacement offset = abs(other - *this); + Displacement offset { abs(other - *this) }; auto minMax = std::minmax(offset.deltaX, offset.deltaY); int min = minMax.first; int max = minMax.second; @@ -134,7 +118,8 @@ struct Point { * @param other Point to which we want the distance * @return Exact magnitude of vector this -> other */ - int ExactDistance(Point other) const + template + int ExactDistance(PointOf other) const { auto vector = *this - other; // No need to call abs() as we square the values anyway @@ -142,21 +127,18 @@ struct Point { return static_cast(std::sqrt(static_cast(vector.deltaX) * vector.deltaX + static_cast(vector.deltaY) * vector.deltaY)); } - constexpr friend Point abs(Point a) - { - return { abs(a.x), abs(a.y) }; - } - - constexpr int ManhattanDistance(Point other) const + template + constexpr int ManhattanDistance(PointOf other) const { - Displacement offset = abs(*this - other); + Displacement offset { abs(*this - other) }; return offset.deltaX + offset.deltaY; } - constexpr int WalkingDistance(Point other) const + template + constexpr int WalkingDistance(PointOf other) const { - Displacement offset = abs(*this - other); + Displacement offset { abs(*this - other) }; return std::max(offset.deltaX, offset.deltaY); } @@ -164,7 +146,7 @@ struct Point { /** * @brief Converts a coordinate in megatiles to the northmost of the 4 corresponding world tiles */ - constexpr Point megaToWorld() const + constexpr PointOf megaToWorld() const { return { 16 + 2 * x, 16 + 2 * y }; } @@ -172,23 +154,71 @@ struct Point { /** * @brief Converts a coordinate in world tiles back to the corresponding megatile */ - constexpr Point worldToMega() const + constexpr PointOf worldToMega() const { return { (x - 16) / 2, (y - 16) / 2 }; } +}; #ifdef BUILD_TESTING - /** - * @brief Format points nicely in test failure messages - * @param stream output stream, expected to have overloads for int and char* - * @param point Object to display - * @return the stream, to allow chaining - */ - friend std::ostream &operator<<(std::ostream &stream, const Point &point) - { - return stream << "(x: " << point.x << ", y: " << point.y << ")"; - } +/** + * @brief Format points nicely in test failure messages + * @param stream output stream, expected to have overloads for int and char* + * @param point Object to display + * @return the stream, to allow chaining + */ +template +std::ostream &operator<<(std::ostream &stream, const PointOf &point) +{ + return stream << "(x: " << point.x << ", y: " << point.y << ")"; +} #endif -}; + +template +constexpr PointOf operator+(PointOf a, DisplacementOf displacement) +{ + a += displacement; + return a; +} + +template +constexpr PointOf operator+(PointOf a, Direction direction) +{ + a += direction; + return a; +} + +template +constexpr DisplacementOf operator-(PointOf a, const PointOf &b) +{ + return { static_cast(a.x - b.x), static_cast(a.y - b.y) }; +} + +template +constexpr PointOf operator-(PointOf a, DisplacementOf displacement) +{ + a -= displacement; + return a; +} + +template +constexpr PointOf operator*(PointOf a, const float factor) +{ + a *= factor; + return a; +} + +template +constexpr PointOf operator*(PointOf a, const int factor) +{ + a *= factor; + return a; +} + +template +constexpr PointOf abs(PointOf a) +{ + return { abs(a.x), abs(a.y) }; +} } // namespace devilution diff --git a/Source/engine/world_tile.hpp b/Source/engine/world_tile.hpp new file mode 100644 index 000000000..fef693d47 --- /dev/null +++ b/Source/engine/world_tile.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "engine/point.hpp" + +namespace devilution { + +using WorldTileCoord = uint8_t; +using WorldTilePosition = PointOf; + +} // namespace devilution diff --git a/Source/lighting.cpp b/Source/lighting.cpp index 26b88f656..6115cf8f3 100644 --- a/Source/lighting.cpp +++ b/Source/lighting.cpp @@ -26,34 +26,32 @@ bool UpdateLighting; namespace { -std::vector CrawlFlips(const std::vector displacements) +std::vector> CrawlFlips(const DisplacementOf *begin, const DisplacementOf *end) { - std::vector ret; - for (auto displacement : displacements) { + std::vector> ret; + for (; begin != end; ++begin) { + DisplacementOf displacement = *begin; if (displacement.deltaX != 0) - ret.push_back(displacement.flipX()); - ret.push_back(displacement); + ret.emplace_back(displacement.flipX()); + ret.emplace_back(displacement); if (displacement.deltaX != 0 && displacement.deltaY != 0) - ret.push_back(displacement.flipXY()); + ret.emplace_back(displacement.flipXY()); if (displacement.deltaY != 0) - ret.push_back(displacement.flipY()); + ret.emplace_back(displacement.flipY()); } return ret; } -std::vector CrawlRow(int row) +std::vector> CrawlRow(int8_t row) { - if (row == 0) - return { { 0, 0 } }; - - std::vector ret; - for (int i = 0; i < row; i++) - ret.push_back({ i, row }); + StaticVector, (CrawlTableSize - 1) * 2 + 1> ret; + for (int8_t i = 0; i < row; i++) + ret.emplace_back(i, row); if (row > 1) - ret.push_back({ row - 1, row - 1 }); - for (int i = 0; i < row; i++) - ret.push_back({ row, i }); - return CrawlFlips(ret); + ret.emplace_back(static_cast(row - 1), static_cast(row - 1)); + for (int8_t i = 0; i < row; i++) + ret.emplace_back(row, i); + return CrawlFlips(ret.begin(), ret.end()); } } // namespace @@ -82,10 +80,12 @@ std::vector CrawlRow(int row) * +-------> x */ -const std::vector> CrawlTable = [] { - std::vector> ret; - for (int i = 0; i < 19; i++) - ret.push_back(CrawlRow(i)); +const std::array>, CrawlTableSize> CrawlTable = []() { + std::array>, CrawlTableSize> ret; + ret[0].emplace_back(0, 0); + for (size_t row = 1; row < CrawlTableSize; ++row) { + ret[row] = CrawlRow(static_cast(row)); + } return ret; }(); diff --git a/Source/lighting.h b/Source/lighting.h index 8e3906bdb..23a92f834 100644 --- a/Source/lighting.h +++ b/Source/lighting.h @@ -76,7 +76,8 @@ void lighting_color_cycling(); /* rdata */ -extern DVL_API_FOR_TEST const std::vector> CrawlTable; +constexpr size_t CrawlTableSize = 19; +extern DVL_API_FOR_TEST const std::array>, CrawlTableSize> CrawlTable; extern const uint8_t VisionCrawlTable[23][30]; } // namespace devilution diff --git a/Source/missiles.cpp b/Source/missiles.cpp index 5cd7fe604..07949aa17 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -1714,7 +1714,7 @@ void AddLightball(Missile &missile, const AddMissileParameter ¶meter) UpdateMissileVelocity(missile, parameter.dst, 16); missile._miAnimFrame = GenerateRnd(8) + 1; missile._mirange = 255; - const Point position { missile._misource < 0 ? missile.position.start : Players[missile._misource].position.tile }; + const Point position { missile._misource < 0 ? missile.position.start : Point(Players[missile._misource].position.tile) }; missile.var1 = position.x; missile.var2 = position.y; } @@ -3346,7 +3346,7 @@ void MI_Chain(Missile &missile) Point dst { missile.var1, missile.var2 }; Direction dir = GetDirection(position, dst); AddMissile(position, dst, dir, MIS_LIGHTCTRL, TARGET_MONSTERS, id, 1, missile._mispllvl); - int rad = std::min(missile._mispllvl + 3, (int)CrawlTable.size() - 1); + int rad = std::min(missile._mispllvl + 3, CrawlTable.size() - 1); for (int i = 1; i < rad; i++) { for (auto displacement : CrawlTable[i]) { Point target = position + displacement; @@ -3809,7 +3809,7 @@ void MI_Element(Missile &missile) if (missile._miAnimType == MFILE_BIGEXP) { ChangeLight(missile._mlid, missile.position.tile, missile._miAnimFrame); - Point startPoint = missile.var3 == 2 ? Point { missile.var4, missile.var5 } : missile.position.start; + Point startPoint = missile.var3 == 2 ? Point { missile.var4, missile.var5 } : Point(missile.position.start); constexpr Displacement Offsets[] = { { 0, 0 }, { 0, 1 }, { 0, -1 }, { 1, 0 }, { 1, -1 }, { 1, 1 }, { -1, 0 }, { -1, 1 }, { -1, -1 } }; for (Displacement offset : Offsets) { if (!CheckBlock(startPoint, missilePosition + offset)) diff --git a/Source/monster.cpp b/Source/monster.cpp index 4507a8b7c..f617c0a5e 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -20,6 +20,7 @@ #include "engine/points_in_rectangle_range.hpp" #include "engine/random.hpp" #include "engine/render/cl2_render.hpp" +#include "engine/world_tile.hpp" #include "init.h" #include "levels/drlg_l1.h" #include "levels/drlg_l4.h" @@ -813,14 +814,14 @@ void StartWalk(int monsterId, int xvel, int yvel, int xadd, int yadd, Direction { auto &monster = Monsters[monsterId]; - int fx = xadd + monster.position.tile.x; - int fy = yadd + monster.position.tile.y; + const auto fx = static_cast(xadd + monster.position.tile.x); + const auto fy = static_cast(yadd + monster.position.tile.y); dMonster[fx][fy] = -(monsterId + 1); monster._mmode = MonsterMode::MoveNorthwards; monster.position.old = monster.position.tile; monster.position.future = { fx, fy }; - monster.position.velocity = { xvel, yvel }; + monster.position.velocity = DisplacementOf { static_cast(xvel), static_cast(yvel) }; monster._mVar1 = xadd; monster._mVar2 = yadd; monster._mVar3 = static_cast(endDir); @@ -832,8 +833,8 @@ void StartWalk2(int monsterId, int xvel, int yvel, int xoff, int yoff, int xadd, { auto &monster = Monsters[monsterId]; - int fx = xadd + monster.position.tile.x; - int fy = yadd + monster.position.tile.y; + const auto fx = static_cast(xadd + monster.position.tile.x); + const auto fy = static_cast(yadd + monster.position.tile.y); dMonster[monster.position.tile.x][monster.position.tile.y] = -(monsterId + 1); monster._mVar1 = monster.position.tile.x; @@ -844,22 +845,22 @@ void StartWalk2(int monsterId, int xvel, int yvel, int xoff, int yoff, int xadd, dMonster[fx][fy] = monsterId + 1; if (monster.mlid != NO_LIGHT) ChangeLightXY(monster.mlid, monster.position.tile); - monster.position.offset = { xoff, yoff }; + monster.position.offset = DisplacementOf { static_cast(xoff), static_cast(yoff) }; monster._mmode = MonsterMode::MoveSouthwards; - monster.position.velocity = { xvel, yvel }; + monster.position.velocity = DisplacementOf { static_cast(xvel), static_cast(yvel) }; monster._mVar3 = static_cast(endDir); NewMonsterAnim(monster, MonsterGraphic::Walk, endDir, AnimationDistributionFlags::ProcessAnimationPending, -1); - monster.position.offset2 = { 16 * xoff, 16 * yoff }; + monster.position.offset2 = DisplacementOf { static_cast(16 * xoff), static_cast(16 * yoff) }; } void StartWalk3(int monsterId, int xvel, int yvel, int xoff, int yoff, int xadd, int yadd, int mapx, int mapy, Direction endDir) { auto &monster = Monsters[monsterId]; - int fx = xadd + monster.position.tile.x; - int fy = yadd + monster.position.tile.y; - int x = mapx + monster.position.tile.x; - int y = mapy + monster.position.tile.y; + const auto fx = static_cast(xadd + monster.position.tile.x); + const auto fy = static_cast(yadd + monster.position.tile.y); + const auto x = static_cast(mapx + monster.position.tile.x); + const auto y = static_cast(mapy + monster.position.tile.y); if (monster.mlid != NO_LIGHT) ChangeLightXY(monster.mlid, { x, y }); @@ -869,14 +870,14 @@ void StartWalk3(int monsterId, int xvel, int yvel, int xoff, int yoff, int xadd, monster.position.temp = { x, y }; monster.position.old = monster.position.tile; monster.position.future = { fx, fy }; - monster.position.offset = { xoff, yoff }; + monster.position.offset = DisplacementOf { static_cast(xoff), static_cast(yoff) }; monster._mmode = MonsterMode::MoveSideways; - monster.position.velocity = { xvel, yvel }; + monster.position.velocity = DisplacementOf { static_cast(xvel), static_cast(yvel) }; monster._mVar1 = fx; monster._mVar2 = fy; monster._mVar3 = static_cast(endDir); NewMonsterAnim(monster, MonsterGraphic::Walk, endDir, AnimationDistributionFlags::ProcessAnimationPending, -1); - monster.position.offset2 = { 16 * xoff, 16 * yoff }; + monster.position.offset2 = DisplacementOf { static_cast(16 * xoff), static_cast(16 * yoff) }; } void StartAttack(Monster &monster) @@ -1236,7 +1237,7 @@ bool MonsterWalk(int monsterId, MonsterMode variant) break; case MonsterMode::MoveSideways: dMonster[monster.position.tile.x][monster.position.tile.y] = 0; - monster.position.tile = { monster._mVar1, monster._mVar2 }; + monster.position.tile = WorldTilePosition { static_cast(monster._mVar1), static_cast(monster._mVar2) }; // dMonster is set here for backwards comparability, without it the monster would be invisible if loaded from a vanilla save. dMonster[monster.position.tile.x][monster.position.tile.y] = monsterId + 1; break; diff --git a/Source/monster.h b/Source/monster.h index 09cde6e31..620045050 100644 --- a/Source/monster.h +++ b/Source/monster.h @@ -15,6 +15,7 @@ #include "engine/cel_sprite.hpp" #include "engine/point.hpp" #include "engine/sound.h" +#include "engine/world_tile.hpp" #include "miniwin/miniwin.h" #include "monstdat.h" #include "spelldat.h" @@ -63,7 +64,7 @@ enum : uint8_t { UMT_NAKRUL, }; -enum class MonsterMode { +enum class MonsterMode : uint8_t { Stand, /** Movement towards N, NW, or NE */ MoveNorthwards, @@ -87,7 +88,7 @@ enum class MonsterMode { Talk, }; -enum class MonsterGraphic { +enum class MonsterGraphic : uint8_t { Stand, Walk, Attack, @@ -166,61 +167,65 @@ struct CMonster { }; struct Monster { // note: missing field _mAFNum - int _mMTidx; - MonsterMode _mmode; - monster_goal _mgoal; + const char *mName; + CMonster *MType; + const MonsterData *MData; + std::unique_ptr uniqueTRN; + AnimationInfo AnimInfo; int _mgoalvar1; int _mgoalvar2; int _mgoalvar3; - uint8_t _pathcount; + int _mVar1; + int _mVar2; + int _mVar3; + int _mmaxhp; + int _mhitpoints; + uint32_t _mFlags; + /** Seed used to determine item drops on death */ + uint32_t _mRndSeed; + /** Seed used to determine AI behaviour/sync sounds in multiplayer games? */ + uint32_t _mAISeed; + + uint16_t mExp; + uint16_t mHit; + uint16_t mHit2; + uint16_t mMagicRes; + _speech_id mtalkmsg; ActorPosition position; + + /** Usually corresponds to the enemy's future position */ + WorldTilePosition enemyPosition; + uint8_t _mMTidx; + MonsterMode _mmode; + monster_goal _mgoal; + uint8_t _pathcount; /** Direction faced by monster (direction enum) */ Direction _mdir; /** The current target of the monster. An index in to either the plr or monster array based on the _meflag value. */ - int _menemy; - /** Usually corresponds to the enemy's future position */ - Point enemyPosition; + uint8_t _menemy; + /** * @brief Contains information for current animation */ - AnimationInfo AnimInfo; bool _mDelFlag; - int _mVar1; - int _mVar2; - int _mVar3; - int _mmaxhp; - int _mhitpoints; + _mai_id _mAi; uint8_t _mint; - uint32_t _mFlags; uint8_t _msquelch; - /** Seed used to determine item drops on death */ - uint32_t _mRndSeed; - /** Seed used to determine AI behaviour/sync sounds in multiplayer games? */ - uint32_t _mAISeed; uint8_t _uniqtype; uint8_t _uniqtrans; int8_t _udeadval; int8_t mWhoHit; int8_t mLevel; - uint16_t mExp; - uint16_t mHit; uint8_t mMinDamage; uint8_t mMaxDamage; - uint16_t mHit2; uint8_t mMinDamage2; uint8_t mMaxDamage2; uint8_t mArmorClass; - uint16_t mMagicRes; - _speech_id mtalkmsg; uint8_t leader; LeaderRelation leaderRelation; uint8_t packsize; int8_t mlid; // BUGFIX -1 is used when not emitting light this should be signed (fixed) - const char *mName; - CMonster *MType; - const MonsterData *MData; - std::unique_ptr uniqueTRN; /** * @brief Sets the current cell sprite to match the desired direction and animation sequence diff --git a/Source/msg.cpp b/Source/msg.cpp index 170cd7510..8f7fa932a 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -18,6 +18,7 @@ #include "dthread.h" #include "encrypt.h" #include "engine/random.hpp" +#include "engine/world_tile.hpp" #include "gamemenu.h" #include "levels/drlg_l1.h" #include "levels/town.h" @@ -57,8 +58,7 @@ struct TMegaPkt { #pragma pack(push, 1) struct DMonsterStr { - uint8_t _mx; - uint8_t _my; + WorldTilePosition position; Direction _mdir; uint8_t _menemy; uint8_t _mactive; @@ -347,7 +347,7 @@ size_t DeltaImportObject(const byte *src, DObjectStr *dst) byte *DeltaExportMonster(byte *dst, const DMonsterStr *src) { for (int i = 0; i < MaxMonsters; i++, src++) { - if (src->_mx == 0xFF) { + if (src->position.x == 0xFF) { *dst++ = byte { 0xFF }; } else { memcpy(dst, src, sizeof(DMonsterStr)); @@ -501,8 +501,8 @@ void DeltaSyncGolem(const TCmdGolem &message, int pnum, uint8_t level) sgbDeltaChanged = true; DMonsterStr &monster = GetDeltaLevel(level).monster[pnum]; - monster._mx = message._mx; - monster._my = message._my; + monster.position.x = message._mx; + monster.position.y = message._my; monster._mactive = UINT8_MAX; monster._menemy = message._menemy; monster._mdir = message._mdir; @@ -527,8 +527,7 @@ void DeltaLeaveSync(uint8_t bLevel) continue; sgbDeltaChanged = true; DMonsterStr &delta = deltaLevel.monster[ma]; - delta._mx = monster.position.tile.x; - delta._my = monster.position.tile.y; + delta.position = monster.position.tile; delta._mdir = monster._mdir; delta._menemy = encode_enemy(monster); delta._mhitpoints = monster._mhitpoints; @@ -2217,8 +2216,7 @@ void delta_kill_monster(int mi, Point position, const Player &player) sgbDeltaChanged = true; DMonsterStr *pD = &GetDeltaLevel(player).monster[mi]; - pD->_mx = position.x; - pD->_my = position.y; + pD->position = position; pD->_mdir = Monsters[mi]._mdir; pD->_mhitpoints = 0; } @@ -2246,8 +2244,8 @@ void delta_sync_monster(const TSyncMonster &monsterSync, uint8_t level) if (monster._mhitpoints == 0) return; - monster._mx = monsterSync._mx; - monster._my = monsterSync._my; + monster.position.x = monsterSync._mx; + monster.position.y = monsterSync._my; monster._mactive = UINT8_MAX; monster._menemy = monsterSync._menemy; monster._mhitpoints = monsterSync._mhitpoints; @@ -2401,16 +2399,17 @@ void DeltaLoadLevel() DLevel &deltaLevel = GetDeltaLevel(localLevel); if (leveltype != DTYPE_TOWN) { for (int i = 0; i < ActiveMonsterCount; i++) { - if (deltaLevel.monster[i]._mx == 0xFF) + if (deltaLevel.monster[i].position.x == 0xFF) continue; M_ClearSquares(i); - int x = deltaLevel.monster[i]._mx; - int y = deltaLevel.monster[i]._my; auto &monster = Monsters[i]; - monster.position.tile = { x, y }; - monster.position.old = { x, y }; - monster.position.future = { x, y }; + { + const WorldTilePosition position = deltaLevel.monster[i].position; + monster.position.tile = position; + monster.position.old = position; + monster.position.future = position; + } if (deltaLevel.monster[i]._mhitpoints != -1) { monster._mhitpoints = deltaLevel.monster[i]._mhitpoints; monster.mWhoHit = deltaLevel.monster[i].mWhoHit; diff --git a/Source/multi.cpp b/Source/multi.cpp index 12872aa91..7a270436e 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -14,6 +14,7 @@ #include "dthread.h" #include "engine/point.hpp" #include "engine/random.hpp" +#include "engine/world_tile.hpp" #include "menu.h" #include "nthread.h" #include "options.h" @@ -349,16 +350,13 @@ void SetupLocalPositions() leveltype = DTYPE_TOWN; setlevel = false; - int x = 75; - int y = 68; - - x += plrxoff[MyPlayerId]; - y += plryoff[MyPlayerId]; + const auto x = static_cast(75 + plrxoff[MyPlayerId]); + const auto y = static_cast(68 + plryoff[MyPlayerId]); Player &myPlayer = *MyPlayer; - myPlayer.position.tile = { x, y }; - myPlayer.position.future = { x, y }; + myPlayer.position.tile = WorldTilePosition { x, y }; + myPlayer.position.future = WorldTilePosition { x, y }; myPlayer.setLevel(currlevel); myPlayer._pLvlChanging = true; myPlayer.pLvlLoad = 0; diff --git a/Source/player.cpp b/Source/player.cpp index 2280e0e72..cc5d4dc46 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -18,6 +18,7 @@ #include "engine/cel_header.hpp" #include "engine/load_file.hpp" #include "engine/random.hpp" +#include "engine/world_tile.hpp" #include "gamemenu.h" #include "init.h" #include "inv_iterators.hpp" @@ -45,13 +46,13 @@ Player Players[MAX_PLRS]; bool MyPlayerIsDead; /** Specifies the X-coordinate delta from the player start location in Tristram. */ -int plrxoff[9] = { 0, 2, 0, 2, 1, 0, 1, 2, 1 }; +int8_t plrxoff[9] = { 0, 2, 0, 2, 1, 0, 1, 2, 1 }; /** Specifies the Y-coordinate delta from the player start location in Tristram. */ -int plryoff[9] = { 0, 2, 2, 0, 1, 1, 0, 1, 2 }; +int8_t plryoff[9] = { 0, 2, 2, 0, 1, 1, 0, 1, 2 }; /** Specifies the X-coordinate delta from a player, used for instance when casting resurrect. */ -int plrxoff2[9] = { 0, 1, 0, 1, 2, 0, 1, 2, 2 }; +int8_t plrxoff2[9] = { 0, 1, 0, 1, 2, 0, 1, 2, 2 }; /** Specifies the Y-coordinate delta from a player, used for instance when casting resurrect. */ -int plryoff2[9] = { 0, 0, 1, 1, 0, 2, 2, 1, 2 }; +int8_t plryoff2[9] = { 0, 0, 1, 1, 0, 2, 2, 1, 2 }; /** Maps from player_class to starting stat in strength. */ int StrengthTbl[enum_size::value] = { @@ -160,9 +161,9 @@ namespace { struct DirectionSettings { Direction dir; - Displacement tileAdd; - Displacement offset; - Displacement map; + DisplacementOf tileAdd; + DisplacementOf offset; + DisplacementOf map; ScrollDirection scrollDir; PLR_MODE walkMode; void (*walkModeHandler)(int, const DirectionSettings &); @@ -221,7 +222,7 @@ void WalkUpwards(int pnum, const DirectionSettings &walkParams) { Player &player = Players[pnum]; dPlayer[player.position.future.x][player.position.future.y] = -(pnum + 1); - player.position.temp = { walkParams.tileAdd.deltaX, walkParams.tileAdd.deltaY }; + player.position.temp = WorldTilePosition { static_cast(walkParams.tileAdd.deltaX), static_cast(walkParams.tileAdd.deltaY) }; } void WalkDownwards(int pnum, const DirectionSettings & /*walkParams*/) @@ -417,7 +418,10 @@ void ChangeOffset(Player &player) player.position.offset2 += player.position.velocity; } - player.position.offset = { player.position.offset2.deltaX >> 8, player.position.offset2.deltaY >> 8 }; + player.position.offset = DisplacementOf { + static_cast(player.position.offset2.deltaX >> 8), + static_cast(player.position.offset2.deltaY >> 8) + }; px -= player.position.offset2.deltaX >> 8; py -= player.position.offset2.deltaY >> 8; @@ -461,7 +465,7 @@ void StartAttack(int pnum, Direction d) SetPlayerOld(player); } -void StartRangeAttack(int pnum, Direction d, int cx, int cy) +void StartRangeAttack(int pnum, Direction d, WorldTileCoord cx, WorldTileCoord cy) { if ((DWORD)pnum >= MAX_PLRS) { app_fatal("StartRangeAttack: illegal player %i", pnum); @@ -488,7 +492,7 @@ void StartRangeAttack(int pnum, Direction d, int cx, int cy) player._pmode = PM_RATTACK; FixPlayerLocation(player, d); SetPlayerOld(player); - player.position.temp = { cx, cy }; + player.position.temp = WorldTilePosition { cx, cy }; } player_graphic GetPlayerGraphicForSpell(spell_id spellId) @@ -503,7 +507,7 @@ player_graphic GetPlayerGraphicForSpell(spell_id spellId) } } -void StartSpell(int pnum, Direction d, int cx, int cy) +void StartSpell(int pnum, Direction d, WorldTileCoord cx, WorldTileCoord cy) { if ((DWORD)pnum >= MAX_PLRS) app_fatal("StartSpell: illegal player %i", pnum); @@ -526,7 +530,7 @@ void StartSpell(int pnum, Direction d, int cx, int cy) FixPlayerLocation(player, d); SetPlayerOld(player); - player.position.temp = { cx, cy }; + player.position.temp = WorldTilePosition { cx, cy }; player.spellLevel = GetSpellLevel(pnum, player._pSpell); } diff --git a/Source/player.h b/Source/player.h index 1aecd9950..b75d85e10 100644 --- a/Source/player.h +++ b/Source/player.h @@ -813,10 +813,10 @@ void PlayDungMsgs(); /* data */ -extern int plrxoff[9]; -extern int plryoff[9]; -extern int plrxoff2[9]; -extern int plryoff2[9]; +extern int8_t plrxoff[9]; +extern int8_t plryoff[9]; +extern int8_t plrxoff2[9]; +extern int8_t plryoff2[9]; extern int StrengthTbl[enum_size::value]; extern int MagicTbl[enum_size::value]; extern int DexterityTbl[enum_size::value]; diff --git a/Source/utils/static_vector.hpp b/Source/utils/static_vector.hpp index 80c98d469..ad3a5ef75 100644 --- a/Source/utils/static_vector.hpp +++ b/Source/utils/static_vector.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -18,6 +19,31 @@ namespace devilution { template class StaticVector { public: + StaticVector() = default; + + template + StaticVector(std::initializer_list elements) + { + for (auto &&element : elements) { + emplace_back(element); + } + } + + [[nodiscard]] const T *begin() const + { + return &(*this)[0]; + } + + [[nodiscard]] const T *end() const + { + return begin() + size_; + } + + [[nodiscard]] size_t size() const + { + return size_; + } + template T &emplace_back(Args &&...args) // NOLINT(readability-identifier-naming) {