Browse Source

Add OptionalCelSprite: smaller than std::optional

`CelSprite` data pointer can never be `nullptr`.

We implement a smaller `optional` for it by taking advantage of that.
pull/4858/merge
Gleb Mazovetskiy 4 years ago
parent
commit
21dc7f553d
  1. 1
      Source/debug.h
  2. 4
      Source/engine/animationinfo.cpp
  3. 7
      Source/engine/animationinfo.h
  4. 82
      Source/engine/cel_sprite.hpp
  5. 4
      Source/engine/render/scrollrt.cpp
  6. 2
      Source/items.cpp
  7. 3
      Source/monster.h
  8. 1
      Source/panels/charpanel.hpp
  9. 1
      Source/panels/info_box.hpp
  10. 6
      Source/player.cpp
  11. 6
      Source/player.h
  12. 1
      Source/quests.h
  13. 1
      Source/utils/stdcompat/optional.hpp

1
Source/debug.h

@ -10,7 +10,6 @@
#include "engine.h"
#include "engine/cel_sprite.hpp"
#include "miniwin/miniwin.h"
#include "utils/stdcompat/optional.hpp"
#include "utils/stdcompat/string_view.hpp"
namespace devilution {

4
Source/engine/animationinfo.cpp

@ -74,7 +74,7 @@ float AnimationInfo::GetAnimationProgress() const
return animationFraction;
}
void AnimationInfo::SetNewAnimation(std::optional<CelSprite> celSprite, int8_t numberOfFrames, int8_t ticksPerFrame, AnimationDistributionFlags flags /*= AnimationDistributionFlags::None*/, int8_t numSkippedFrames /*= 0*/, int8_t distributeFramesBeforeFrame /*= 0*/, float previewShownGameTickFragments /*= 0.F*/)
void AnimationInfo::SetNewAnimation(OptionalCelSprite celSprite, int8_t numberOfFrames, int8_t ticksPerFrame, AnimationDistributionFlags flags /*= AnimationDistributionFlags::None*/, int8_t numSkippedFrames /*= 0*/, int8_t distributeFramesBeforeFrame /*= 0*/, float previewShownGameTickFragments /*= 0.F*/)
{
if ((flags & AnimationDistributionFlags::RepeatedAction) == AnimationDistributionFlags::RepeatedAction && distributeFramesBeforeFrame != 0 && NumberOfFrames == numberOfFrames && CurrentFrame + 1 >= distributeFramesBeforeFrame && CurrentFrame != NumberOfFrames - 1) {
// We showed the same Animation (for example a melee attack) before but truncated the Animation.
@ -167,7 +167,7 @@ void AnimationInfo::SetNewAnimation(std::optional<CelSprite> celSprite, int8_t n
}
}
void AnimationInfo::ChangeAnimationData(std::optional<CelSprite> celSprite, int8_t numberOfFrames, int8_t ticksPerFrame)
void AnimationInfo::ChangeAnimationData(OptionalCelSprite celSprite, int8_t numberOfFrames, int8_t ticksPerFrame)
{
if (numberOfFrames != NumberOfFrames || ticksPerFrame != TicksPerFrame) {
// Ensure that the CurrentFrame is still valid and that we disable ADL cause the calculcated values (for example TickModifier) could be wrong

7
Source/engine/animationinfo.h

@ -9,7 +9,6 @@
#include <type_traits>
#include "engine/cel_sprite.hpp"
#include "utils/stdcompat/optional.hpp"
namespace devilution {
@ -40,7 +39,7 @@ public:
/**
* @brief Animation sprite
*/
std::optional<CelSprite> celSprite;
OptionalCelSprite celSprite;
/**
* @brief How many game ticks are needed to advance one Animation Frame
*/
@ -83,7 +82,7 @@ public:
* @param distributeFramesBeforeFrame Distribute the numSkippedFrames only before this frame
* @param previewShownGameTickFragments Defines how long (in game ticks fraction) the preview animation was shown
*/
void SetNewAnimation(std::optional<CelSprite> celSprite, int8_t numberOfFrames, int8_t ticksPerFrame, AnimationDistributionFlags flags = AnimationDistributionFlags::None, int8_t numSkippedFrames = 0, int8_t distributeFramesBeforeFrame = 0, float previewShownGameTickFragments = 0.F);
void SetNewAnimation(OptionalCelSprite celSprite, int8_t numberOfFrames, int8_t ticksPerFrame, AnimationDistributionFlags flags = AnimationDistributionFlags::None, int8_t numSkippedFrames = 0, int8_t distributeFramesBeforeFrame = 0, float previewShownGameTickFragments = 0.F);
/**
* @brief Changes the Animation Data on-the-fly. This is needed if a animation is currently in progress and the player changes his gear.
@ -91,7 +90,7 @@ public:
* @param numberOfFrames Number of Frames in Animation
* @param ticksPerFrame How many game ticks are needed to advance one Animation Frame
*/
void ChangeAnimationData(std::optional<CelSprite> celSprite, int8_t numberOfFrames, int8_t ticksPerFrame);
void ChangeAnimationData(OptionalCelSprite celSprite, int8_t numberOfFrames, int8_t ticksPerFrame);
/**
* @brief Process the Animation for a game tick (for example advances the frame)

82
Source/engine/cel_sprite.hpp

@ -3,12 +3,15 @@
#include <memory>
#include <utility>
#include "appfat.h"
#include "utils/pointer_value_union.hpp"
#include "utils/stdcompat/cstddef.hpp"
#include "utils/stdcompat/optional.hpp"
namespace devilution {
class OwnedCelSprite;
class OptionalCelSprite;
/**
* Stores a CEL or CL2 sprite and its width(s).
@ -61,8 +64,82 @@ public:
}
private:
// for OptionalCelSprite
CelSprite()
: data_ptr_(nullptr)
, width_(nullptr)
{
}
const byte *data_ptr_;
PointerOrValue<uint16_t> width_;
friend class OptionalCelSprite;
};
/**
* @brief Equivalent to `std::optional<CelSprite>` but smaller.
*/
class OptionalCelSprite {
public:
OptionalCelSprite() = default;
OptionalCelSprite(CelSprite sprite)
: sprite_(sprite)
{
}
explicit OptionalCelSprite(const OwnedCelSprite &owned);
OptionalCelSprite(std::nullopt_t)
: OptionalCelSprite()
{
}
template <typename... Args>
CelSprite &emplace(Args &&...args)
{
sprite_ = CelSprite(std::forward<Args>(args)...);
return sprite_;
}
OptionalCelSprite &operator=(CelSprite sprite)
{
sprite_ = sprite;
return *this;
}
OptionalCelSprite &operator=(std::nullopt_t)
{
sprite_ = {};
return *this;
}
CelSprite operator*() const
{
assert(sprite_.data_ptr_ != nullptr);
return sprite_;
}
CelSprite *operator->()
{
assert(sprite_.data_ptr_ != nullptr);
return &sprite_;
}
const CelSprite *operator->() const
{
assert(sprite_.data_ptr_ != nullptr);
return &sprite_;
}
operator bool() const
{
return sprite_.data_ptr_ != nullptr;
}
private:
CelSprite sprite_;
};
/**
@ -103,6 +180,11 @@ inline CelSprite::CelSprite(const OwnedCelSprite &owned)
{
}
inline OptionalCelSprite::OptionalCelSprite(const OwnedCelSprite &owned)
{
sprite_ = CelSprite { owned };
}
struct CelSpriteWithFrameHeight {
CelSprite sprite;
unsigned frameHeight;

4
Source/engine/render/scrollrt.cpp

@ -509,7 +509,7 @@ void DrawPlayer(const Surface &out, const Player &player, Point tilePosition, Po
return;
}
std::optional<CelSprite> sprite = player.AnimInfo.celSprite;
OptionalCelSprite sprite = player.AnimInfo.celSprite;
int nCel = player.AnimInfo.GetFrameToUseForRendering();
if (player.previewCelSprite) {
@ -713,7 +713,7 @@ void DrawItem(const Surface &out, Point tilePosition, Point targetBufferPosition
if (item._iPostDraw == pre)
return;
std::optional<CelSprite> cel = item.AnimInfo.celSprite;
OptionalCelSprite cel = item.AnimInfo.celSprite;
if (!cel) {
Log("Draw Item \"{}\" 1: NULL CelSprite", item._iIName);
return;

2
Source/items.cpp

@ -4582,7 +4582,7 @@ void Item::setNewAnimation(bool showAnimation)
{
int8_t it = ItemCAnimTbl[_iCurs];
int8_t numberOfFrames = ItemAnimLs[it];
auto celSprite = itemanims[it] ? std::optional<CelSprite> { *itemanims[it] } : std::nullopt;
auto celSprite = itemanims[it] ? OptionalCelSprite { *itemanims[it] } : std::nullopt;
if (_iCurs != ICURS_MAGIC_ROCK)
AnimInfo.SetNewAnimation(celSprite, numberOfFrames, 1, AnimationDistributionFlags::ProcessAnimationPending, 0, numberOfFrames);
else

3
Source/monster.h

@ -20,7 +20,6 @@
#include "monstdat.h"
#include "spelldat.h"
#include "textdat.h"
#include "utils/stdcompat/optional.hpp"
namespace devilution {
@ -133,7 +132,7 @@ enum class LeaderRelation : uint8_t {
};
struct AnimStruct {
[[nodiscard]] std::optional<CelSprite> getCelSpritesForDirection(Direction direction) const
[[nodiscard]] OptionalCelSprite getCelSpritesForDirection(Direction direction) const
{
const byte *spriteData = celSpritesForDirections[static_cast<size_t>(direction)];
if (spriteData == nullptr)

1
Source/panels/charpanel.hpp

@ -2,7 +2,6 @@
#include "engine/cel_sprite.hpp"
#include "engine/surface.hpp"
#include "utils/stdcompat/optional.hpp"
namespace devilution {

1
Source/panels/info_box.hpp

@ -1,7 +1,6 @@
#pragma once
#include "engine/cel_sprite.hpp"
#include "utils/stdcompat/optional.hpp"
namespace devilution {

6
Source/player.cpp

@ -368,7 +368,7 @@ void StartWalk(int pnum, Displacement vel, Direction dir, bool pmWillBeCalled)
StartWalkAnimation(player, dir, pmWillBeCalled);
}
void SetPlayerGPtrs(const char *path, std::unique_ptr<byte[]> &data, std::array<std::optional<CelSprite>, 8> &anim, int width)
void SetPlayerGPtrs(const char *path, std::unique_ptr<byte[]> &data, std::array<OptionalCelSprite, 8> &anim, int width)
{
data = nullptr;
data = LoadFileInMem(path);
@ -2188,7 +2188,7 @@ void Player::UpdatePreviewCelSprite(_cmd_id cmdId, Point point, uint16_t wParam1
return;
LoadPlrGFX(*this, *graphic);
std::optional<CelSprite> celSprites = AnimationData[static_cast<size_t>(*graphic)].GetCelSpritesForDirection(dir);
OptionalCelSprite celSprites = AnimationData[static_cast<size_t>(*graphic)].GetCelSpritesForDirection(dir);
if (celSprites && previewCelSprite != celSprites) {
previewCelSprite = celSprites;
progressToNextGameTickWhenPreviewWasSet = gfProgressToNextGameTick;
@ -2343,7 +2343,7 @@ void NewPlrAnim(Player &player, player_graphic graphic, Direction dir, int8_t nu
{
LoadPlrGFX(player, graphic);
std::optional<CelSprite> celSprite = player.AnimationData[static_cast<size_t>(graphic)].GetCelSpritesForDirection(dir);
OptionalCelSprite celSprite = player.AnimationData[static_cast<size_t>(graphic)].GetCelSpritesForDirection(dir);
float previewShownGameTickFragments = 0.F;
if (celSprite == player.previewCelSprite && !player.IsWalking())

6
Source/player.h

@ -196,14 +196,14 @@ struct PlayerAnimationData {
/**
* @brief CelSprites for the different directions
*/
std::array<std::optional<CelSprite>, 8> CelSpritesForDirections;
std::array<OptionalCelSprite, 8> CelSpritesForDirections;
/**
* @brief Raw Data (binary) of the CL2 file.
* Is referenced from CelSprite in celSpritesForDirections
*/
std::unique_ptr<byte[]> RawData;
[[nodiscard]] std::optional<CelSprite> GetCelSpritesForDirection(Direction direction) const
[[nodiscard]] OptionalCelSprite GetCelSpritesForDirection(Direction direction) const
{
return CelSpritesForDirections[static_cast<size_t>(direction)];
}
@ -234,7 +234,7 @@ struct Player {
/**
* @brief Contains a optional preview CelSprite that is displayed until the current command is handled by the game logic
*/
std::optional<CelSprite> previewCelSprite;
OptionalCelSprite previewCelSprite;
/**
* @brief Contains the progress to next game tick when previewCelSprite was set
*/

1
Source/quests.h

@ -16,7 +16,6 @@
#include "panels/info_box.hpp"
#include "textdat.h"
#include "utils/attributes.h"
#include "utils/stdcompat/optional.hpp"
namespace devilution {

1
Source/utils/stdcompat/optional.hpp

@ -7,6 +7,7 @@
#include <experimental/optional> // IWYU pragma: export
#define optional experimental::optional
#define nullopt experimental::nullopt
#define nullopt_t experimental::nullopt_t
#else
#error "Missing support for <optional> or <experimental/optional>"
#endif

Loading…
Cancel
Save