Browse Source

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<int8_t>

* CrawlTable: vector<vector> -> array<vector>

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
pull/4852/head
Gleb Mazovetskiy 4 years ago committed by GitHub
parent
commit
3b2e560fa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      Source/engine/actor_position.hpp
  2. 266
      Source/engine/displacement.hpp
  3. 180
      Source/engine/point.hpp
  4. 10
      Source/engine/world_tile.hpp
  5. 44
      Source/lighting.cpp
  6. 3
      Source/lighting.h
  7. 6
      Source/missiles.cpp
  8. 33
      Source/monster.cpp
  9. 63
      Source/monster.h
  10. 33
      Source/msg.cpp
  11. 12
      Source/multi.cpp
  12. 30
      Source/player.cpp
  13. 8
      Source/player.h
  14. 26
      Source/utils/static_vector.hpp

19
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<int8_t> offset;
/** Same as offset but contains the value in a higher range */
Displacement offset2;
DisplacementOf<int16_t> 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<int16_t> velocity;
};
} // namespace devilution

266
Source/engine/displacement.hpp

@ -11,143 +11,104 @@
namespace devilution {
struct Displacement {
int deltaX;
int deltaY;
template <typename DeltaT>
struct DisplacementOf;
Displacement() = default;
using Displacement = DisplacementOf<int>;
constexpr Displacement(int deltaX, int deltaY)
template <typename DeltaT>
struct DisplacementOf {
DeltaT deltaX;
DeltaT deltaY;
DisplacementOf() = default;
template <typename DisplacementDeltaT>
constexpr DisplacementOf(DisplacementOf<DisplacementDeltaT> 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 <typename DisplacementDeltaT>
constexpr bool operator==(const DisplacementOf<DisplacementDeltaT> &other) const
{
return deltaX == other.deltaX && deltaY == other.deltaY;
}
constexpr bool operator!=(const Displacement &other) const
template <typename DisplacementDeltaT>
constexpr bool operator!=(const DisplacementOf<DisplacementDeltaT> &other) const
{
return !(*this == other);
}
constexpr Displacement &operator+=(const Displacement &displacement)
template <typename DisplacementDeltaT = DeltaT>
constexpr DisplacementOf<DeltaT> &operator+=(DisplacementOf<DisplacementDeltaT> displacement)
{
deltaX += displacement.deltaX;
deltaY += displacement.deltaY;
return *this;
}
constexpr Displacement &operator-=(const Displacement &displacement)
template <typename DisplacementDeltaT = DeltaT>
constexpr DisplacementOf<DeltaT> &operator-=(DisplacementOf<DisplacementDeltaT> displacement)
{
deltaX -= displacement.deltaX;
deltaY -= displacement.deltaY;
return *this;
}
constexpr Displacement &operator*=(const int factor)
constexpr DisplacementOf<DeltaT> &operator*=(const int factor)
{
deltaX *= factor;
deltaY *= factor;
return *this;
}
constexpr Displacement &operator*=(const float factor)
constexpr DisplacementOf<DeltaT> &operator*=(const float factor)
{
deltaX = static_cast<int>(deltaX * factor);
deltaY = static_cast<int>(deltaY * factor);
deltaX = static_cast<DeltaT>(deltaX * factor);
deltaY = static_cast<DeltaT>(deltaY * factor);
return *this;
}
constexpr Displacement &operator/=(const int factor)
constexpr DisplacementOf<DeltaT> &operator/=(const int factor)
{
deltaX /= factor;
deltaY /= factor;
return *this;
}
constexpr Displacement &operator/=(const float factor)
constexpr DisplacementOf<DeltaT> &operator/=(const float factor)
{
deltaX = static_cast<int>(deltaX / factor);
deltaY = static_cast<int>(deltaY / factor);
deltaX = static_cast<DeltaT>(deltaX / factor);
deltaY = static_cast<DeltaT>(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<float>(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<DeltaT> 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<DeltaT> 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<DeltaT> screenToMissile() const
{
return -screenToWorld();
}
constexpr Displacement screenToLight() const
constexpr DisplacementOf<DeltaT> 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<int>(deltaY) - static_cast<int>(deltaX), -(static_cast<int>(deltaY) + static_cast<int>(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<DeltaT> Rotate(int quadrants) const
{
constexpr int Sines[] = { 0, 1, 0, -1 };
static_assert(std::is_signed<DeltaT>::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<DeltaT> flipX() const
{
return { deltaX, -deltaY };
static_assert(std::is_signed<DeltaT>::value, "DeltaT must be signed for flipX");
return { static_cast<DeltaT>(-deltaX), deltaY };
}
constexpr Displacement flipXY()
[[nodiscard]] constexpr DisplacementOf<DeltaT> flipY() const
{
return { -deltaX, -deltaY };
static_assert(std::is_signed<DeltaT>::value, "DeltaT must be signed for flipY");
return { deltaX, static_cast<DeltaT>(-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<DeltaT> flipXY() const
{
return stream << "(x: " << offset.deltaX << ", y: " << offset.deltaY << ")";
static_assert(std::is_signed<DeltaT>::value, "DeltaT must be signed for flipXY");
return { static_cast<DeltaT>(-deltaX), static_cast<DeltaT>(-deltaY) };
}
#endif
private:
static constexpr Displacement fromDirection(Direction direction)
static constexpr DisplacementOf<DeltaT> fromDirection(Direction direction)
{
static_assert(std::is_signed<DeltaT>::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 <typename DisplacementDeltaT>
std::ostream &operator<<(std::ostream &stream, const DisplacementOf<DisplacementDeltaT> &offset)
{
return stream << "(x: " << offset.deltaX << ", y: " << offset.deltaY << ")";
}
#endif
template <typename DisplacementDeltaT, typename OtherDisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> operator+(DisplacementOf<DisplacementDeltaT> a, DisplacementOf<OtherDisplacementDeltaT> b)
{
a += b;
return a;
}
template <typename DisplacementDeltaT, typename OtherDisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> operator-(DisplacementOf<DisplacementDeltaT> a, DisplacementOf<OtherDisplacementDeltaT> b)
{
a -= b;
return a;
}
template <typename DisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> operator*(DisplacementOf<DisplacementDeltaT> a, const int factor)
{
a *= factor;
return a;
}
template <typename DisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> operator*(DisplacementOf<DisplacementDeltaT> a, const float factor)
{
a *= factor;
return a;
}
template <typename DisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> operator/(DisplacementOf<DisplacementDeltaT> a, const int factor)
{
a /= factor;
return a;
}
template <typename DisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> operator/(DisplacementOf<DisplacementDeltaT> a, const float factor)
{
a /= factor;
return a;
}
template <typename DisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> operator-(DisplacementOf<DisplacementDeltaT> a)
{
return { -a.deltaX, -a.deltaY };
}
template <typename DisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> operator<<(DisplacementOf<DisplacementDeltaT> a, unsigned factor)
{
return { a.deltaX << factor, a.deltaY << factor };
}
template <typename DisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> operator>>(DisplacementOf<DisplacementDeltaT> a, unsigned factor)
{
return { a.deltaX >> factor, a.deltaY >> factor };
}
template <typename DisplacementDeltaT>
constexpr DisplacementOf<DisplacementDeltaT> abs(DisplacementOf<DisplacementDeltaT> a)
{
return { abs(a.deltaX), abs(a.deltaY) };
}
template <typename DeltaT>
Displacement DisplacementOf<DeltaT>::normalized() const
{
const float magnitude = this->magnitude();
Displacement normalDisplacement = Displacement(*this) << 16u;
normalDisplacement /= magnitude;
return normalDisplacement;
}
} // namespace devilution

180
Source/engine/point.hpp

@ -1,6 +1,7 @@
#pragma once
#include <cmath>
#include <type_traits>
#ifdef BUILD_TESTING
#include <ostream>
#endif
@ -12,99 +13,81 @@
namespace devilution {
struct Point {
int x;
int y;
template <typename CoordT>
struct PointOf;
Point() = default;
using Point = PointOf<int>;
constexpr Point(int x, int y)
template <typename CoordT>
struct PointOf {
CoordT x;
CoordT y;
PointOf() = default;
template <typename PointCoordT>
constexpr PointOf(PointOf<PointCoordT> 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 <typename PointCoordT>
constexpr bool operator==(const PointOf<PointCoordT> &other) const
{
return x == other.x && y == other.y;
}
constexpr bool operator!=(const Point &other) const
template <typename PointCoordT>
constexpr bool operator!=(const PointOf<PointCoordT> &other) const
{
return !(*this == other);
}
constexpr Point &operator+=(const Displacement &displacement)
template <typename DisplacementDeltaT = int>
constexpr PointOf<CoordT> &operator+=(const DisplacementOf<DisplacementDeltaT> &displacement)
{
x += displacement.deltaX;
y += displacement.deltaY;
return *this;
}
constexpr Point &operator+=(Direction direction)
constexpr PointOf<CoordT> &operator+=(Direction direction)
{
return (*this) += Displacement(direction);
return (*this) += DisplacementOf<typename std::make_signed<CoordT>::type>(direction);
}
constexpr Point &operator-=(const Displacement &displacement)
template <typename DisplacementDeltaT = int>
constexpr PointOf<CoordT> &operator-=(const DisplacementOf<DisplacementDeltaT> &displacement)
{
x -= displacement.deltaX;
y -= displacement.deltaY;
return *this;
}
constexpr Point &operator*=(const float factor)
constexpr PointOf<CoordT> &operator*=(const float factor)
{
x = static_cast<int>(x * factor);
y = static_cast<int>(y * factor);
return *this;
}
constexpr Point &operator*=(const int factor)
constexpr PointOf<CoordT> &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<CoordT> 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 <typename PointCoordT>
constexpr int ApproxDistance(PointOf<PointCoordT> 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 <typename PointCoordT>
int ExactDistance(PointOf<PointCoordT> 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<int>(std::sqrt(static_cast<int64_t>(vector.deltaX) * vector.deltaX + static_cast<int64_t>(vector.deltaY) * vector.deltaY));
}
constexpr friend Point abs(Point a)
{
return { abs(a.x), abs(a.y) };
}
constexpr int ManhattanDistance(Point other) const
template <typename PointCoordT>
constexpr int ManhattanDistance(PointOf<PointCoordT> other) const
{
Displacement offset = abs(*this - other);
Displacement offset { abs(*this - other) };
return offset.deltaX + offset.deltaY;
}
constexpr int WalkingDistance(Point other) const
template <typename PointCoordT>
constexpr int WalkingDistance(PointOf<PointCoordT> other) const
{
Displacement offset = abs(*this - other);
Displacement offset { abs(*this - other) };
return std::max<int>(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<CoordT> 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<CoordT> 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 <typename PointCoordT>
std::ostream &operator<<(std::ostream &stream, const PointOf<PointCoordT> &point)
{
return stream << "(x: " << point.x << ", y: " << point.y << ")";
}
#endif
};
template <typename PointCoordT, typename DisplacementDeltaT>
constexpr PointOf<PointCoordT> operator+(PointOf<PointCoordT> a, DisplacementOf<DisplacementDeltaT> displacement)
{
a += displacement;
return a;
}
template <typename PointCoordT>
constexpr PointOf<PointCoordT> operator+(PointOf<PointCoordT> a, Direction direction)
{
a += direction;
return a;
}
template <typename PointCoordT, typename OtherPointCoordT>
constexpr DisplacementOf<PointCoordT> operator-(PointOf<PointCoordT> a, const PointOf<OtherPointCoordT> &b)
{
return { static_cast<PointCoordT>(a.x - b.x), static_cast<PointCoordT>(a.y - b.y) };
}
template <typename PointCoordT, typename DisplacementDeltaT>
constexpr PointOf<PointCoordT> operator-(PointOf<PointCoordT> a, DisplacementOf<DisplacementDeltaT> displacement)
{
a -= displacement;
return a;
}
template <typename PointCoordT>
constexpr PointOf<PointCoordT> operator*(PointOf<PointCoordT> a, const float factor)
{
a *= factor;
return a;
}
template <typename PointCoordT>
constexpr PointOf<PointCoordT> operator*(PointOf<PointCoordT> a, const int factor)
{
a *= factor;
return a;
}
template <typename PointCoordT>
constexpr PointOf<PointCoordT> abs(PointOf<PointCoordT> a)
{
return { abs(a.x), abs(a.y) };
}
} // namespace devilution

10
Source/engine/world_tile.hpp

@ -0,0 +1,10 @@
#pragma once
#include "engine/point.hpp"
namespace devilution {
using WorldTileCoord = uint8_t;
using WorldTilePosition = PointOf<WorldTileCoord>;
} // namespace devilution

44
Source/lighting.cpp

@ -26,34 +26,32 @@ bool UpdateLighting;
namespace {
std::vector<Displacement> CrawlFlips(const std::vector<Displacement> displacements)
std::vector<DisplacementOf<int8_t>> CrawlFlips(const DisplacementOf<int8_t> *begin, const DisplacementOf<int8_t> *end)
{
std::vector<Displacement> ret;
for (auto displacement : displacements) {
std::vector<DisplacementOf<int8_t>> ret;
for (; begin != end; ++begin) {
DisplacementOf<int8_t> 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<Displacement> CrawlRow(int row)
std::vector<DisplacementOf<int8_t>> CrawlRow(int8_t row)
{
if (row == 0)
return { { 0, 0 } };
std::vector<Displacement> ret;
for (int i = 0; i < row; i++)
ret.push_back({ i, row });
StaticVector<DisplacementOf<int8_t>, (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<int8_t>(row - 1), static_cast<int8_t>(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<Displacement> CrawlRow(int row)
* +-------> x
*/
const std::vector<std::vector<Displacement>> CrawlTable = [] {
std::vector<std::vector<Displacement>> ret;
for (int i = 0; i < 19; i++)
ret.push_back(CrawlRow(i));
const std::array<std::vector<DisplacementOf<int8_t>>, CrawlTableSize> CrawlTable = []() {
std::array<std::vector<DisplacementOf<int8_t>>, CrawlTableSize> ret;
ret[0].emplace_back(0, 0);
for (size_t row = 1; row < CrawlTableSize; ++row) {
ret[row] = CrawlRow(static_cast<int8_t>(row));
}
return ret;
}();

3
Source/lighting.h

@ -76,7 +76,8 @@ void lighting_color_cycling();
/* rdata */
extern DVL_API_FOR_TEST const std::vector<std::vector<Displacement>> CrawlTable;
constexpr size_t CrawlTableSize = 19;
extern DVL_API_FOR_TEST const std::array<std::vector<DisplacementOf<int8_t>>, CrawlTableSize> CrawlTable;
extern const uint8_t VisionCrawlTable[23][30];
} // namespace devilution

6
Source/missiles.cpp

@ -1714,7 +1714,7 @@ void AddLightball(Missile &missile, const AddMissileParameter &parameter)
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<int>(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))

33
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<WorldTileCoord>(xadd + monster.position.tile.x);
const auto fy = static_cast<WorldTileCoord>(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<int16_t> { static_cast<int16_t>(xvel), static_cast<int16_t>(yvel) };
monster._mVar1 = xadd;
monster._mVar2 = yadd;
monster._mVar3 = static_cast<int>(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<WorldTileCoord>(xadd + monster.position.tile.x);
const auto fy = static_cast<WorldTileCoord>(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<int16_t> { static_cast<int16_t>(xoff), static_cast<int16_t>(yoff) };
monster._mmode = MonsterMode::MoveSouthwards;
monster.position.velocity = { xvel, yvel };
monster.position.velocity = DisplacementOf<int16_t> { static_cast<int16_t>(xvel), static_cast<int16_t>(yvel) };
monster._mVar3 = static_cast<int>(endDir);
NewMonsterAnim(monster, MonsterGraphic::Walk, endDir, AnimationDistributionFlags::ProcessAnimationPending, -1);
monster.position.offset2 = { 16 * xoff, 16 * yoff };
monster.position.offset2 = DisplacementOf<int16_t> { static_cast<int16_t>(16 * xoff), static_cast<int16_t>(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<WorldTileCoord>(xadd + monster.position.tile.x);
const auto fy = static_cast<WorldTileCoord>(yadd + monster.position.tile.y);
const auto x = static_cast<WorldTileCoord>(mapx + monster.position.tile.x);
const auto y = static_cast<WorldTileCoord>(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<int16_t> { static_cast<int16_t>(xoff), static_cast<int16_t>(yoff) };
monster._mmode = MonsterMode::MoveSideways;
monster.position.velocity = { xvel, yvel };
monster.position.velocity = DisplacementOf<int16_t> { static_cast<int16_t>(xvel), static_cast<int16_t>(yvel) };
monster._mVar1 = fx;
monster._mVar2 = fy;
monster._mVar3 = static_cast<int>(endDir);
NewMonsterAnim(monster, MonsterGraphic::Walk, endDir, AnimationDistributionFlags::ProcessAnimationPending, -1);
monster.position.offset2 = { 16 * xoff, 16 * yoff };
monster.position.offset2 = DisplacementOf<int16_t> { static_cast<int16_t>(16 * xoff), static_cast<int16_t>(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<WorldTileCoord>(monster._mVar1), static_cast<WorldTileCoord>(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;

63
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<uint8_t[]> 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<uint8_t[]> uniqueTRN;
/**
* @brief Sets the current cell sprite to match the desired direction and animation sequence

33
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;

12
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<WorldTileCoord>(75 + plrxoff[MyPlayerId]);
const auto y = static_cast<WorldTileCoord>(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;

30
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<HeroClass>::value] = {
@ -160,9 +161,9 @@ namespace {
struct DirectionSettings {
Direction dir;
Displacement tileAdd;
Displacement offset;
Displacement map;
DisplacementOf<int8_t> tileAdd;
DisplacementOf<int8_t> offset;
DisplacementOf<int8_t> 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<WorldTileCoord>(walkParams.tileAdd.deltaX), static_cast<WorldTileCoord>(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<int8_t> {
static_cast<int8_t>(player.position.offset2.deltaX >> 8),
static_cast<int8_t>(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);
}

8
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<HeroClass>::value];
extern int MagicTbl[enum_size<HeroClass>::value];
extern int DexterityTbl[enum_size<HeroClass>::value];

26
Source/utils/static_vector.hpp

@ -1,6 +1,7 @@
#pragma once
#include <cstddef>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
@ -18,6 +19,31 @@ namespace devilution {
template <class T, size_t N>
class StaticVector {
public:
StaticVector() = default;
template <typename U>
StaticVector(std::initializer_list<U> 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 <typename... Args>
T &emplace_back(Args &&...args) // NOLINT(readability-identifier-naming)
{

Loading…
Cancel
Save