You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

371 lines
13 KiB

#include <gtest/gtest.h>
#include "nthread.h"
#include "player.h"
using namespace devilution;
/**
* @brief Represents a Action in the game_logic or rendering.
*/
struct TestData {
virtual ~TestData() = default;
};
/**
* @brief Represents a GameTick, this includes skipping of Frames (for example because of Fastest Attack Modifier) and ProcessAnimation (which also updates Animation Frame/Count).
*/
struct GameTickData : TestData {
int _ExpectedAnimationFrame;
int _ExpectedAnimationCnt;
int _FramesToSkip;
GameTickData(int expectedAnimationFrame, int expectedAnimationCnt, int framesToSkip = 0)
{
_ExpectedAnimationFrame = expectedAnimationFrame;
_ExpectedAnimationCnt = expectedAnimationCnt;
_FramesToSkip = framesToSkip;
}
};
/**
* @brief Represents a rendering/drawing Action.
* This happens directly after the game_logic (_fProgressToNextGameTick = 0) or if no GameTick is due between two GameTicks (_fProgressToNextGameTick != 0).
*/
struct RenderingData : TestData {
float _fProgressToNextGameTick;
int _ExpectedRenderingFrame;
RenderingData(float fProgressToNextGameTick, int expectedRenderingFrame)
{
this->_fProgressToNextGameTick = fProgressToNextGameTick;
this->_ExpectedRenderingFrame = expectedRenderingFrame;
}
};
void RunAnimationTest(int numFrames, int delay, AnimationFlags flags, int numSkippedFrames, int distributeFramesBeforeFrame, const std::vector<TestData *> &vecTestData)
{
const int pnum = 0;
PlayerStruct *pPlayer = &plr[pnum];
NewPlrAnim(pnum, nullptr, numFrames, delay, 128, flags, numSkippedFrames, distributeFramesBeforeFrame);
int currentGameTick = 0;
for (TestData *x : vecTestData) {
auto gameTickData = dynamic_cast<GameTickData *>(x);
if (gameTickData != nullptr) {
currentGameTick += 1;
if (gameTickData->_FramesToSkip != 0)
pPlayer->_pAnimFrame += gameTickData->_FramesToSkip;
ProcessPlayerAnimation(pnum);
EXPECT_EQ(pPlayer->_pAnimFrame, gameTickData->_ExpectedAnimationFrame);
EXPECT_EQ(pPlayer->_pAnimCnt, gameTickData->_ExpectedAnimationCnt);
}
auto renderingData = dynamic_cast<RenderingData *>(x);
if (renderingData != nullptr) {
gfProgressToNextGameTick = renderingData->_fProgressToNextGameTick;
EXPECT_EQ(GetFrameToUseForPlayerRendering(pPlayer), renderingData->_ExpectedRenderingFrame)
<< std::fixed << std::setprecision(2)
<< "ProgressToNextGameTick: " << renderingData->_fProgressToNextGameTick
<< " AnimFrame: " << pPlayer->_pAnimFrame
<< " AnimCnt: " << pPlayer->_pAnimCnt
<< " GameTick: " << currentGameTick;
}
}
for (TestData *x : vecTestData) {
delete x;
}
}
TEST(AnimationInformation, AttackSwordWarrior) // ProcessAnimationPending should be considered by distribution logic
{
RunAnimationTest(16, 0, AnimationFlags::ProcessAnimationPending, 0, 9,
{
new GameTickData(2, 0), // ProcessPlayer directly after StartAttack (in same GameTick). So we don't see any rendering before.
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.0f, 3),
new RenderingData(0.3f, 3),
new RenderingData(0.6f, 3),
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 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.6f, 8),
new RenderingData(0.8f, 8),
new GameTickData(9, 0), // After this GameTick, the Animation Distribution Logic is disabled
new RenderingData(0.1f, 9),
new GameTickData(10, 0),
new RenderingData(0.4f, 10),
new GameTickData(11, 0),
new RenderingData(0.4f, 11),
new GameTickData(12, 0),
new RenderingData(0.3f, 12),
new GameTickData(13, 0),
new RenderingData(0.0f, 13),
new GameTickData(14, 0),
new RenderingData(0.6f, 14),
new GameTickData(15, 0),
new RenderingData(0.6f, 15),
new GameTickData(16, 0),
new RenderingData(0.6f, 16),
// Animation stopped cause PM_DoAttack would stop the Animation "if (plr[pnum]._pAnimFrame == plr[pnum]._pAFrames) {"
});
}
TEST(AnimationInformation, AttackSwordWarriorWithFastestAttack) // Skipped frames and ProcessAnimationPending should be considered by distribution logic
{
RunAnimationTest(16, 0, AnimationFlags::ProcessAnimationPending, 2, 9,
{
new GameTickData(2, 0), // ProcessPlayer directly after StartAttack (in same GameTick). So we don't see any rendering before.
new RenderingData(0.0f, 1),
new RenderingData(0.3f, 1),
new RenderingData(0.6f, 1),
new RenderingData(0.8f, 2),
new GameTickData(3, 0),
new RenderingData(0.0f, 2),
new RenderingData(0.3f, 3),
new RenderingData(0.6f, 3),
new RenderingData(0.8f, 3),
new GameTickData(4, 0),
new RenderingData(0.0f, 4),
new RenderingData(0.3f, 4),
new RenderingData(0.6f, 5),
new RenderingData(0.8f, 5),
new GameTickData(7, 0, 2),
new RenderingData(0.0f, 5),
new RenderingData(0.3f, 6),
new RenderingData(0.6f, 6),
new RenderingData(0.8f, 7),
new GameTickData(8, 0),
new RenderingData(0.0f, 7),
new RenderingData(0.3f, 7),
new RenderingData(0.6f, 8),
new RenderingData(0.8f, 8),
new GameTickData(9, 0), // After this GameTick, the Animation Distribution Logic is disabled
new RenderingData(0.1f, 9),
new GameTickData(10, 0),
new RenderingData(0.4f, 10),
new GameTickData(11, 0),
new RenderingData(0.4f, 11),
new GameTickData(12, 0),
new RenderingData(0.3f, 12),
new GameTickData(13, 0),
new RenderingData(0.0f, 13),
new GameTickData(14, 0),
new RenderingData(0.6f, 14),
new GameTickData(15, 0),
new RenderingData(0.6f, 15),
new GameTickData(16, 0),
new RenderingData(0.6f, 16),
// Animation stopped cause PM_DoAttack would stop the Animation "if (plr[pnum]._pAnimFrame == plr[pnum]._pAFrames) {"
});
}
TEST(AnimationInformation, BlockingWarriorNormal) // Ignored delay for last Frame should be considered by distribution logic
{
RunAnimationTest(2, 2, AnimationFlags::SkipsDelayOfLastFrame, 0, 0,
{
new RenderingData(0.0f, 1),
new RenderingData(0.3f, 1),
new RenderingData(0.6f, 1),
new RenderingData(0.8f, 1),
new GameTickData(1, 1),
new RenderingData(0.0f, 1),
new RenderingData(0.3f, 1),
new RenderingData(0.6f, 1),
new RenderingData(0.8f, 1),
new GameTickData(1, 2),
new RenderingData(0.0f, 2),
new RenderingData(0.3f, 2),
new RenderingData(0.6f, 2),
new RenderingData(0.8f, 2),
new GameTickData(2, 0),
new RenderingData(0.0f, 2),
new RenderingData(0.3f, 2),
new RenderingData(0.6f, 2),
new RenderingData(0.8f, 2),
// Animation stopped cause PM_DoBlock would stop the Animation "if (plr[pnum]._pAnimFrame >= plr[pnum]._pBFrames) {"
});
}
TEST(AnimationInformation, BlockingSorcererWithFastBlock) // Skipped frames and ignored delay for last Frame should be considered by distribution logic
{
RunAnimationTest(6, 2, AnimationFlags::SkipsDelayOfLastFrame, 4, 0,
{
new RenderingData(0.0f, 1),
new RenderingData(0.3f, 1),
new RenderingData(0.6f, 1),
new RenderingData(0.8f, 2),
new GameTickData(1, 1),
new RenderingData(0.0f, 2),
new RenderingData(0.3f, 2),
new RenderingData(0.6f, 3),
new RenderingData(0.8f, 3),
new GameTickData(1, 2),
new RenderingData(0.0f, 4),
new RenderingData(0.3f, 4),
new RenderingData(0.6f, 4),
new RenderingData(0.8f, 5),
new GameTickData(2, 0),
new RenderingData(0.0f, 5),
new RenderingData(0.3f, 5),
new RenderingData(0.6f, 6),
new RenderingData(0.8f, 6),
// Animation stopped cause PM_DoBlock would stop the Animation "if (plr[pnum]._pAnimFrame >= plr[pnum]._pBFrames) {"
});
}
TEST(AnimationInformation, HitRecoverySorcererZenMode) // Skipped frames and ignored delay for last Frame should be considered by distribution logic
{
RunAnimationTest(8, 0, AnimationFlags::None, 4, 0,
{
new RenderingData(0.0f, 1),
new RenderingData(0.3f, 1),
new RenderingData(0.6f, 2),
new RenderingData(0.8f, 2),
new GameTickData(3, 0, 1),
new RenderingData(0.0f, 3),
new RenderingData(0.3f, 3),
new RenderingData(0.6f, 4),
new RenderingData(0.8f, 4),
new GameTickData(7, 0, 3),
new RenderingData(0.0f, 5),
new RenderingData(0.3f, 5),
new RenderingData(0.6f, 6),
new RenderingData(0.8f, 6),
new GameTickData(8, 0),
new RenderingData(0.0f, 7),
new RenderingData(0.3f, 7),
new RenderingData(0.6f, 8),
new RenderingData(0.8f, 8),
// Animation stopped cause PM_DoGotHit would stop the Animation "if (plr[pnum]._pAnimFrame >= plr[pnum]._pHFrames) {"
});
}
TEST(AnimationInformation, Stand) // Distribution Logic shouldn't change anything here
{
RunAnimationTest(10, 3, AnimationFlags::None, 0, 0,
{
new RenderingData(0.1f, 1),
new GameTickData(1, 1),
new RenderingData(0.6f, 1),
new GameTickData(1, 2),
new RenderingData(0.6f, 1),
new GameTickData(1, 3),
new RenderingData(0.6f, 1),
new GameTickData(2, 0),
new RenderingData(0.6f, 2),
new GameTickData(2, 1),
new RenderingData(0.6f, 2),
new GameTickData(2, 2),
new RenderingData(0.6f, 2),
new GameTickData(2, 3),
new RenderingData(0.6f, 2),
new GameTickData(3, 0),
new RenderingData(0.6f, 3),
new GameTickData(3, 1),
new RenderingData(0.6f, 3),
new GameTickData(3, 2),
new RenderingData(0.6f, 3),
new GameTickData(3, 3),
new RenderingData(0.6f, 3),
new GameTickData(4, 0),
new RenderingData(0.6f, 4),
new GameTickData(4, 1),
new RenderingData(0.6f, 4),
new GameTickData(4, 2),
new RenderingData(0.6f, 4),
new GameTickData(4, 3),
new RenderingData(0.6f, 4),
new GameTickData(5, 0),
new RenderingData(0.6f, 5),
new GameTickData(5, 1),
new RenderingData(0.6f, 5),
new GameTickData(5, 2),
new RenderingData(0.6f, 5),
new GameTickData(5, 3),
new RenderingData(0.6f, 5),
new GameTickData(6, 0),
new RenderingData(0.6f, 6),
new GameTickData(6, 1),
new RenderingData(0.6f, 6),
new GameTickData(6, 2),
new RenderingData(0.6f, 6),
new GameTickData(6, 3),
new RenderingData(0.6f, 6),
new GameTickData(7, 0),
new RenderingData(0.6f, 7),
new GameTickData(7, 1),
new RenderingData(0.6f, 7),
new GameTickData(7, 2),
new RenderingData(0.6f, 7),
new GameTickData(7, 3),
new RenderingData(0.6f, 7),
new GameTickData(8, 0),
new RenderingData(0.6f, 8),
new GameTickData(8, 1),
new RenderingData(0.6f, 8),
new GameTickData(8, 2),
new RenderingData(0.6f, 8),
new GameTickData(8, 3),
new RenderingData(0.6f, 8),
new GameTickData(9, 0),
new RenderingData(0.6f, 9),
new GameTickData(9, 1),
new RenderingData(0.6f, 9),
new GameTickData(9, 2),
new RenderingData(0.6f, 9),
new GameTickData(9, 3),
new RenderingData(0.6f, 9),
new GameTickData(10, 0),
new RenderingData(0.6f, 10),
new GameTickData(10, 1),
new RenderingData(0.6f, 10),
new GameTickData(10, 2),
new RenderingData(0.6f, 10),
new GameTickData(10, 3),
new RenderingData(0.6f, 10),
new GameTickData(1, 0), // Animation starts again
new RenderingData(0.1f, 1),
new GameTickData(1, 1),
new RenderingData(0.6f, 1),
new GameTickData(1, 2),
new RenderingData(0.6f, 1),
new GameTickData(1, 3),
new RenderingData(0.6f, 1),
});
}