From b08e0c20dcf8afcb7a4f67e6927599b66f8703f6 Mon Sep 17 00:00:00 2001 From: obligaron Date: Sun, 2 May 2021 14:54:40 +0200 Subject: [PATCH] In repeated Animations show skipped Frames from previous Animation (repeated Attack/RangeAttack/Spell) --- Source/engine/animationinfo.cpp | 20 ++++++++++++++++- Source/engine/animationinfo.h | 4 ++++ test/animationinfo_test.cpp | 40 ++++++++++++++++----------------- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/Source/engine/animationinfo.cpp b/Source/engine/animationinfo.cpp index 3ad4be1ec..854d636eb 100644 --- a/Source/engine/animationinfo.cpp +++ b/Source/engine/animationinfo.cpp @@ -30,7 +30,14 @@ int AnimationInfo::GetFrameToUseForRendering() const // 1 added for rounding reasons. float to int cast always truncate. int absoluteAnimationFrame = 1 + (int)(totalTicksForCurrentAnimationSequence * TickModifier); - if (absoluteAnimationFrame > RelevantFramesForDistributing) { + if (SkippedFramesFromPreviousAnimation > 0) { + // absoluteAnimationFrames contains also the Frames from the previous Animation, so if we want to get the current Frame we have to remove them + absoluteAnimationFrame -= SkippedFramesFromPreviousAnimation; + if (absoluteAnimationFrame <= 0) { + // We still display the remains of the previous Animation + absoluteAnimationFrame = NumberOfFrames + absoluteAnimationFrame; + } + } else if (absoluteAnimationFrame > RelevantFramesForDistributing) { // this can happen if we are at the last frame and the next game tick is due (gfProgressToNextGameTick >= 1.0f) if (absoluteAnimationFrame > (RelevantFramesForDistributing + 1)) { // we should never have +2 frames even if next game tick is due @@ -47,6 +54,14 @@ int AnimationInfo::GetFrameToUseForRendering() const void AnimationInfo::SetNewAnimation(uint8_t *pData, int numberOfFrames, int delayLen, AnimationDistributionParams params /*= AnimationDistributionParams::None*/, int numSkippedFrames /*= 0*/, int distributeFramesBeforeFrame /*= 0*/) { + if (pData == this->pData && distributeFramesBeforeFrame != 0 && NumberOfFrames == numberOfFrames && CurrentFrame >= distributeFramesBeforeFrame && CurrentFrame != NumberOfFrames) { + // We showed the same Animation (for example a melee attack) before but truncated the Animation. + // So now we should add them back to the new Animation. This increases the speed of the current Animation but the game logic/ticks isn't affected. + SkippedFramesFromPreviousAnimation = NumberOfFrames - CurrentFrame; + } else { + SkippedFramesFromPreviousAnimation = 0; + } + this->pData = pData; NumberOfFrames = numberOfFrames; CurrentFrame = 1; @@ -108,6 +123,9 @@ void AnimationInfo::SetNewAnimation(uint8_t *pData, int numberOfFrames, int dela relevantAnimationTicksWithSkipping -= delayLen; } + // The truncated Frames from previous Animation will also be shown, so we also have to distribute them for the given time (game ticks) + relevantAnimationTicksForDistribution += (SkippedFramesFromPreviousAnimation * ticksPerFrame); + // if we skipped Frames we need to expand the game ticks to make one game tick for this Animation "faster" float tickModifier = (float)relevantAnimationTicksForDistribution / (float)relevantAnimationTicksWithSkipping; diff --git a/Source/engine/animationinfo.h b/Source/engine/animationinfo.h index a84f20106..c59b7e8cd 100644 --- a/Source/engine/animationinfo.h +++ b/Source/engine/animationinfo.h @@ -85,6 +85,10 @@ private: * @brief Animation Frames that will be adjusted for the skipped Frames/game ticks */ int RelevantFramesForDistributing; + /** + * @brief Animation Frames that wasn't shown from previous Animation + */ + int SkippedFramesFromPreviousAnimation; }; } // namespace devilution diff --git a/test/animationinfo_test.cpp b/test/animationinfo_test.cpp index b63b322db..563162d9e 100644 --- a/test/animationinfo_test.cpp +++ b/test/animationinfo_test.cpp @@ -278,38 +278,38 @@ TEST(AnimationInfo, AttackSwordWarriorRepeated) new SetNewAnimationData(16, 0, AnimationDistributionParams::ProcessAnimationPending, 0, 9), // ProcessAnimation directly after StartAttack (in same GameTick). So we don't see any rendering before. new GameTickData(2, 0), + new RenderingData(0.0f, 11), + new RenderingData(0.3f, 11), + new RenderingData(0.6f, 12), + new RenderingData(0.8f, 12), + new GameTickData(3, 0), + new RenderingData(0.0f, 13), + new RenderingData(0.3f, 13), + new RenderingData(0.6f, 14), + new RenderingData(0.8f, 14), + new GameTickData(4, 0), + new RenderingData(0.0f, 15), + new RenderingData(0.3f, 15), + new RenderingData(0.6f, 16), + new RenderingData(0.8f, 16), + new GameTickData(5, 0), new RenderingData(0.0f, 1), new RenderingData(0.3f, 1), - new RenderingData(0.6f, 1), - new RenderingData(0.8f, 1), - new GameTickData(3, 0), - new RenderingData(0.0f, 2), - new RenderingData(0.3f, 2), new RenderingData(0.6f, 2), - new RenderingData(0.8f, 3), - new GameTickData(4, 0), + new RenderingData(0.8f, 2), + new GameTickData(6, 0), new RenderingData(0.0f, 3), new RenderingData(0.3f, 3), - new RenderingData(0.6f, 3), + new RenderingData(0.6f, 4), new RenderingData(0.8f, 4), - new GameTickData(5, 0), - new RenderingData(0.0f, 4), - new RenderingData(0.3f, 4), - new RenderingData(0.6f, 5), - new RenderingData(0.8f, 5), - new GameTickData(6, 0), + new GameTickData(7, 0), new RenderingData(0.0f, 5), new RenderingData(0.3f, 5), new RenderingData(0.6f, 6), new RenderingData(0.8f, 6), - new GameTickData(7, 0), - new RenderingData(0.0f, 6), - new RenderingData(0.3f, 7), - new RenderingData(0.6f, 7), - new RenderingData(0.8f, 7), new GameTickData(8, 0), new RenderingData(0.0f, 7), - new RenderingData(0.3f, 8), + new RenderingData(0.3f, 7), new RenderingData(0.6f, 8), new RenderingData(0.8f, 8),