diff --git a/Source/engine/animationinfo.cpp b/Source/engine/animationinfo.cpp index cf8a24da6..c6b57a354 100644 --- a/Source/engine/animationinfo.cpp +++ b/Source/engine/animationinfo.cpp @@ -5,7 +5,47 @@ */ #include "animationinfo.h" +#include "appfat.h" +#include "nthread.h" +#include "utils/log.hpp" namespace devilution { +int AnimationInfo::GetFrameToUseForRendering() +{ + // Normal logic is used, + // - if no frame-skipping is required and so we have exactly one Animationframe per GameTick + // or + // - if we load from a savegame where the new variables are not stored (we don't want to break savegame compatiblity because of smoother rendering of one animation) + int relevantAnimationFramesForDistributing = RelevantFramesForDistributing; + if (relevantAnimationFramesForDistributing <= 0) + return CurrentFrame; + + if (CurrentFrame > relevantAnimationFramesForDistributing) + return CurrentFrame; + + assert(GameTicksSinceSequenceStarted >= 0); + + float progressToNextGameTick = gfProgressToNextGameTick; + + // we don't use the processed game ticks alone but also the fragtion of the next game tick (if a rendering happens between game ticks). This helps to smooth the animations. + float totalGameTicksForCurrentAnimationSequence = progressToNextGameTick + (float)GameTicksSinceSequenceStarted; + + // 1 added for rounding reasons. float to int cast always truncate. + int absoluteAnimationFrame = 1 + (int)(totalGameTicksForCurrentAnimationSequence * GameTickModifier); + if (absoluteAnimationFrame > relevantAnimationFramesForDistributing) { + // this can happen if we are at the last frame and the next game tick is due (nthread_GetProgressToNextGameTick returns 1.0f) + if (absoluteAnimationFrame > (relevantAnimationFramesForDistributing + 1)) { + // we should never have +2 frames even if next game tick is due + Log("GetFrameToUseForRendering: Calculated an invalid Animation Frame (Calculated {} MaxFrame {})", absoluteAnimationFrame, relevantAnimationFramesForDistributing); + } + return relevantAnimationFramesForDistributing; + } + if (absoluteAnimationFrame <= 0) { + Log("GetFrameToUseForRendering: Calculated an invalid Animation Frame (Calculated {})", absoluteAnimationFrame); + return 1; + } + return absoluteAnimationFrame; +} + } // namespace devilution diff --git a/Source/engine/animationinfo.h b/Source/engine/animationinfo.h index f45e8a87e..306fe5efd 100644 --- a/Source/engine/animationinfo.h +++ b/Source/engine/animationinfo.h @@ -34,6 +34,13 @@ public: * @brief Current frame of animation */ int CurrentFrame; + + /** + * @brief Calculates the Frame to use for the Animation rendering + * @return The Frame to use for rendering + */ + int GetFrameToUseForRendering(); + /* * @brief Specifies how many animations-fractions are displayed between two gameticks. this can be > 0, if animations are skipped or < 0 if the same animation is shown in multiple times (delay specified). */ diff --git a/Source/player.cpp b/Source/player.cpp index c54f13c72..a0216094b 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -15,7 +15,6 @@ #include "loadsave.h" #include "minitext.h" #include "missiles.h" -#include "nthread.h" #include "options.h" #include "qol/autopickup.h" #include "spells.h" @@ -3726,43 +3725,6 @@ void ProcessPlayerAnimation(int pnum) } } -int GetFrameToUseForPlayerRendering(const PlayerStruct *pPlayer) -{ - // Normal logic is used, - // - if no frame-skipping is required and so we have exactly one Animationframe per GameTick - // or - // - if we load from a savegame where the new variables are not stored (we don't want to break savegame compatiblity because of smoother rendering of one animation) - int relevantAnimationFramesForDistributing = pPlayer->AnimInfo.RelevantFramesForDistributing; - if (relevantAnimationFramesForDistributing <= 0) - return pPlayer->AnimInfo.CurrentFrame; - - if (pPlayer->AnimInfo.CurrentFrame > relevantAnimationFramesForDistributing) - return pPlayer->AnimInfo.CurrentFrame; - - assert(pPlayer->AnimInfo.GameTicksSinceSequenceStarted >= 0); - - float progressToNextGameTick = gfProgressToNextGameTick; - - // we don't use the processed game ticks alone but also the fragtion of the next game tick (if a rendering happens between game ticks). This helps to smooth the animations. - float totalGameTicksForCurrentAnimationSequence = progressToNextGameTick + (float)pPlayer->AnimInfo.GameTicksSinceSequenceStarted; - - // 1 added for rounding reasons. float to int cast always truncate. - int absoluteAnimationFrame = 1 + (int)(totalGameTicksForCurrentAnimationSequence * pPlayer->AnimInfo.GameTickModifier); - if (absoluteAnimationFrame > relevantAnimationFramesForDistributing) { - // this can happen if we are at the last frame and the next game tick is due (nthread_GetProgressToNextGameTick returns 1.0f) - if (absoluteAnimationFrame > (relevantAnimationFramesForDistributing + 1)) { - // we should never have +2 frames even if next game tick is due - Log("GetFrameToUseForPlayerRendering: Calculated an invalid Animation Frame (Calculated {} MaxFrame {})", absoluteAnimationFrame, relevantAnimationFramesForDistributing); - } - return relevantAnimationFramesForDistributing; - } - if (absoluteAnimationFrame <= 0) { - Log("GetFrameToUseForPlayerRendering: Calculated an invalid Animation Frame (Calculated {})", absoluteAnimationFrame); - return 1; - } - return absoluteAnimationFrame; -} - void ClrPlrPath(int pnum) { if ((DWORD)pnum >= MAX_PLRS) { diff --git a/Source/player.h b/Source/player.h index d51051aa5..8c55156e6 100644 --- a/Source/player.h +++ b/Source/player.h @@ -415,12 +415,6 @@ enum class AnimationDistributionParams : uint8_t { void NewPlrAnim(int pnum, BYTE *Peq, int numFrames, int Delay, int width, AnimationDistributionParams params = AnimationDistributionParams::None, int numSkippedFrames = 0, int distributeFramesBeforeFrame = 0); void SetPlrAnims(int pnum); void ProcessPlayerAnimation(int pnum); -/** - * @brief Calculates the Frame to use for the Animation rendering - * @param pPlayer Player - * @return The Frame to use for rendering - */ -int GetFrameToUseForPlayerRendering(const PlayerStruct *pPlayer); void CreatePlayer(int pnum, HeroClass c); int CalcStatDiff(int pnum); #ifdef _DEBUG diff --git a/Source/scrollrt.cpp b/Source/scrollrt.cpp index 124fd2bbf..86feaf270 100644 --- a/Source/scrollrt.cpp +++ b/Source/scrollrt.cpp @@ -417,7 +417,7 @@ static void DrawPlayer(const CelOutputBuffer &out, int pnum, int x, int y, int p PlayerStruct *pPlayer = &plr[pnum]; BYTE *pCelBuff = pPlayer->AnimInfo.pData; - int nCel = GetFrameToUseForPlayerRendering(pPlayer); + int nCel = pPlayer->AnimInfo.GetFrameToUseForRendering(); int nWidth = pPlayer->_pAnimWidth; if (pCelBuff == nullptr) { diff --git a/test/animationinfo_test.cpp b/test/animationinfo_test.cpp index 79ff8aafb..e1eaaa4f3 100644 --- a/test/animationinfo_test.cpp +++ b/test/animationinfo_test.cpp @@ -71,7 +71,7 @@ void RunAnimationTest(int numFrames, int delay, AnimationDistributionParams para auto renderingData = dynamic_cast(x); if (renderingData != nullptr) { gfProgressToNextGameTick = renderingData->_fProgressToNextGameTick; - EXPECT_EQ(GetFrameToUseForPlayerRendering(pPlayer), renderingData->_ExpectedRenderingFrame) + EXPECT_EQ(pPlayer->AnimInfo.GetFrameToUseForRendering(), renderingData->_ExpectedRenderingFrame) << std::fixed << std::setprecision(2) << "ProgressToNextGameTick: " << renderingData->_fProgressToNextGameTick << " CurrentFrame: " << pPlayer->AnimInfo.CurrentFrame