From 0b5183f63ea00aa5adf485a5811331594eb94358 Mon Sep 17 00:00:00 2001 From: obligaron Date: Wed, 28 Apr 2021 05:37:58 +0200 Subject: [PATCH] Introduce AnimationInfo Update AnimationInfo.DelayLen comment --- CMakeLists.txt | 3 +- Source/engine/animationinfo.cpp | 11 ++ Source/engine/animationinfo.h | 51 ++++++ Source/loadsave.cpp | 16 +- Source/msg.cpp | 4 +- Source/multi.cpp | 4 +- Source/player.cpp | 154 +++++++++--------- Source/player.h | 20 +-- Source/scrollrt.cpp | 2 +- ...mation_test.cpp => animationinfo_test.cpp} | 32 ++-- test/player_test.cpp | 4 +- test/writehero_test.cpp | 8 +- 12 files changed, 180 insertions(+), 129 deletions(-) create mode 100644 Source/engine/animationinfo.cpp create mode 100644 Source/engine/animationinfo.h rename test/{animationinformation_test.cpp => animationinfo_test.cpp} (89%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ae89a8aa..501380453 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -343,6 +343,7 @@ set(devilutionx_SRCS Source/controls/modifier_hints.cpp Source/controls/plrctrls.cpp Source/controls/touch.cpp + Source/engine/animationinfo.cpp Source/qol/autopickup.cpp Source/qol/common.cpp Source/qol/monhealthbar.cpp @@ -453,7 +454,7 @@ if(RUN_TESTS) test/scrollrt_test.cpp test/stores_test.cpp test/writehero_test.cpp - test/animationinformation_test.cpp) + test/animationinfo_test.cpp) endif() add_executable(${BIN_TARGET} WIN32 MACOSX_BUNDLE ${devilutionx_SRCS}) diff --git a/Source/engine/animationinfo.cpp b/Source/engine/animationinfo.cpp new file mode 100644 index 000000000..cf8a24da6 --- /dev/null +++ b/Source/engine/animationinfo.cpp @@ -0,0 +1,11 @@ +/** + * @file animationinfo.cpp + * + * Contains the core animation information and related logic + */ + +#include "animationinfo.h" + +namespace devilution { + +} // namespace devilution diff --git a/Source/engine/animationinfo.h b/Source/engine/animationinfo.h new file mode 100644 index 000000000..c3b4ebd8c --- /dev/null +++ b/Source/engine/animationinfo.h @@ -0,0 +1,51 @@ +/** + * @file animationinfo.h + * + * Contains the core animation information and related logic + */ +#pragma once + +#include + +namespace devilution { + +/* +* @brief Contains the core animation information and related logic +*/ +class AnimationInfo { +public: + /* + * @brief Pointer to Animation Data + */ + uint8_t *_pAnimData; + /* + * @brief Additional delay of each animation in the current animation + */ + int _pAnimDelay; + /* + * @brief Increases by one each game tick, counting how close we are to _pAnimDelay + */ + int _pAnimCnt; + /* + * @brief Number of frames in current animation + */ + int _pAnimLen; + /* + * @brief Current frame of animation + */ + int _pAnimFrame; + /* + * @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). + */ + float _pAnimGameTickModifier; + /* + * @brief Number of GameTicks after the current animation sequence started + */ + int _pAnimGameTicksSinceSequenceStarted; + /* + * @brief Animation Frames that will be adjusted for the skipped Frames/GameTicks + */ + int _pAnimRelevantAnimationFramesForDistributing; +}; + +} // namespace devilution diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index 8a24fba52..51994bf2d 100644 --- a/Source/loadsave.cpp +++ b/Source/loadsave.cpp @@ -343,10 +343,10 @@ static void LoadPlayer(LoadHelper *file, int p) file->skip(4); // Unused pPlayer->_pgfxnum = file->nextLE(); file->skip(4); // Skip pointer _pAnimData - pPlayer->_pAnimDelay = file->nextLE(); - pPlayer->_pAnimCnt = file->nextLE(); - pPlayer->_pAnimLen = file->nextLE(); - pPlayer->_pAnimFrame = file->nextLE(); + pPlayer->AnimInfo._pAnimDelay = file->nextLE(); + pPlayer->AnimInfo._pAnimCnt = file->nextLE(); + pPlayer->AnimInfo._pAnimLen = file->nextLE(); + pPlayer->AnimInfo._pAnimFrame = file->nextLE(); pPlayer->_pAnimWidth = file->nextLE(); // Skip _pAnimWidth2 file->skip(4); @@ -1327,10 +1327,10 @@ static void SavePlayer(SaveHelper *file, int p) file->skip(4); // Unused file->writeLE(pPlayer->_pgfxnum); file->skip(4); // Skip pointer _pAnimData - file->writeLE(pPlayer->_pAnimDelay); - file->writeLE(pPlayer->_pAnimCnt); - file->writeLE(pPlayer->_pAnimLen); - file->writeLE(pPlayer->_pAnimFrame); + file->writeLE(pPlayer->AnimInfo._pAnimDelay); + file->writeLE(pPlayer->AnimInfo._pAnimCnt); + file->writeLE(pPlayer->AnimInfo._pAnimLen); + file->writeLE(pPlayer->AnimInfo._pAnimFrame); file->writeLE(pPlayer->_pAnimWidth); // write _pAnimWidth2 for vanilla compatibility file->writeLE(CalculateWidth2(pPlayer->_pAnimWidth)); diff --git a/Source/msg.cpp b/Source/msg.cpp index 7f424a44e..1920703d3 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -2235,8 +2235,8 @@ static DWORD On_PLAYER_JOINLEVEL(TCmd *pCmd, int pnum) LoadPlrGFX(pnum, PFILE_DEATH); plr[pnum]._pmode = PM_DEATH; NewPlrAnim(pnum, plr[pnum]._pDAnim[DIR_S], plr[pnum]._pDFrames, 1, plr[pnum]._pDWidth); - plr[pnum]._pAnimFrame = plr[pnum]._pAnimLen - 1; - plr[pnum].actionFrame = plr[pnum]._pAnimLen * 2; + plr[pnum].AnimInfo._pAnimFrame = plr[pnum].AnimInfo._pAnimLen - 1; + plr[pnum].actionFrame = plr[pnum].AnimInfo._pAnimLen * 2; dFlags[plr[pnum].position.tile.x][plr[pnum].position.tile.y] |= BFLAG_DEAD_PLAYER; } diff --git a/Source/multi.cpp b/Source/multi.cpp index 1be25ebbb..2223ad9cc 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -880,8 +880,8 @@ void recv_plrinfo(int pnum, TCmdPlrInfoHdr *p, bool recv) LoadPlrGFX(pnum, PFILE_DEATH); plr[pnum]._pmode = PM_DEATH; NewPlrAnim(pnum, plr[pnum]._pDAnim[DIR_S], plr[pnum]._pDFrames, 1, plr[pnum]._pDWidth); - plr[pnum]._pAnimFrame = plr[pnum]._pAnimLen - 1; - plr[pnum].actionFrame = 2 * plr[pnum]._pAnimLen; + plr[pnum].AnimInfo._pAnimFrame = plr[pnum].AnimInfo._pAnimLen - 1; + plr[pnum].actionFrame = 2 * plr[pnum].AnimInfo._pAnimLen; dFlags[plr[pnum].position.tile.x][plr[pnum].position.tile.y] |= BFLAG_DEAD_PLAYER; } } diff --git a/Source/player.cpp b/Source/player.cpp index ae54bf881..f7dd7da0e 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -544,15 +544,15 @@ void NewPlrAnim(int pnum, BYTE *Peq, int numFrames, int Delay, int width, Animat app_fatal("NewPlrAnim: illegal player %d", pnum); } - plr[pnum]._pAnimData = Peq; - plr[pnum]._pAnimLen = numFrames; - plr[pnum]._pAnimFrame = 1; - plr[pnum]._pAnimCnt = 0; - plr[pnum]._pAnimDelay = Delay; + plr[pnum].AnimInfo._pAnimData = Peq; + plr[pnum].AnimInfo._pAnimLen = numFrames; + plr[pnum].AnimInfo._pAnimFrame = 1; + plr[pnum].AnimInfo._pAnimCnt = 0; + plr[pnum].AnimInfo._pAnimDelay = Delay; plr[pnum]._pAnimWidth = width; - plr[pnum]._pAnimGameTicksSinceSequenceStarted = 0; - plr[pnum]._pAnimRelevantAnimationFramesForDistributing = 0; - plr[pnum]._pAnimGameTickModifier = 0.0f; + plr[pnum].AnimInfo._pAnimGameTicksSinceSequenceStarted = 0; + plr[pnum].AnimInfo._pAnimRelevantAnimationFramesForDistributing = 0; + plr[pnum].AnimInfo._pAnimGameTickModifier = 0.0f; if (numSkippedFrames != 0 || params != AnimationDistributionParams::None) { // Animation Frames that will be adjusted for the skipped Frames/GameTicks @@ -583,7 +583,7 @@ void NewPlrAnim(int pnum, BYTE *Peq, int numFrames, int Delay, int width, Animat // The Animation Distribution Logic needs to account how many GameTicks passed since the Animation started. // Because ProcessAnimation will increase this later (in same GameTick as NewPlrAnim), we correct this upfront. // This also means Rendering should never hapen with _pAnimGameTicksSinceSequenceStarted < 0. - plr[pnum]._pAnimGameTicksSinceSequenceStarted = -1; + plr[pnum].AnimInfo._pAnimGameTicksSinceSequenceStarted = -1; } if (params == AnimationDistributionParams::SkipsDelayOfLastFrame) { @@ -612,8 +612,8 @@ void NewPlrAnim(int pnum, BYTE *Peq, int numFrames, int Delay, int width, Animat // gameTickModifier specifies the Animation fraction per GameTick, so we have to remove the delay from the variable gameTickModifier /= gameTicksPerFrame; - plr[pnum]._pAnimRelevantAnimationFramesForDistributing = relevantAnimationFramesForDistributing; - plr[pnum]._pAnimGameTickModifier = gameTickModifier; + plr[pnum].AnimInfo._pAnimRelevantAnimationFramesForDistributing = relevantAnimationFramesForDistributing; + plr[pnum].AnimInfo._pAnimGameTickModifier = gameTickModifier; } } @@ -1135,13 +1135,13 @@ void InitPlayer(int pnum, bool FirstTime) if (plr[pnum]._pHitPoints >> 6 > 0) { plr[pnum]._pmode = PM_STAND; NewPlrAnim(pnum, plr[pnum]._pNAnim[DIR_S], plr[pnum]._pNFrames, 3, plr[pnum]._pNWidth); - plr[pnum]._pAnimFrame = GenerateRnd(plr[pnum]._pNFrames - 1) + 1; - plr[pnum]._pAnimCnt = GenerateRnd(3); + plr[pnum].AnimInfo._pAnimFrame = GenerateRnd(plr[pnum]._pNFrames - 1) + 1; + plr[pnum].AnimInfo._pAnimCnt = GenerateRnd(3); } else { plr[pnum]._pmode = PM_DEATH; NewPlrAnim(pnum, plr[pnum]._pDAnim[DIR_S], plr[pnum]._pDFrames, 1, plr[pnum]._pDWidth); - plr[pnum]._pAnimFrame = plr[pnum]._pAnimLen - 1; - plr[pnum].actionFrame = 2 * plr[pnum]._pAnimLen; + plr[pnum].AnimInfo._pAnimFrame = plr[pnum].AnimInfo._pAnimLen - 1; + plr[pnum].actionFrame = 2 * plr[pnum].AnimInfo._pAnimLen; } plr[pnum]._pdir = DIR_S; @@ -2275,21 +2275,21 @@ bool PM_DoWalk(int pnum, int variant) //Play walking sound effect on certain animation frames if (sgOptions.Audio.bWalkingSound) { - if (plr[pnum]._pAnimFrame == 3 - || (plr[pnum]._pWFrames == 8 && plr[pnum]._pAnimFrame == 7) - || (plr[pnum]._pWFrames != 8 && plr[pnum]._pAnimFrame == 4)) { + if (plr[pnum].AnimInfo._pAnimFrame == 3 + || (plr[pnum]._pWFrames == 8 && plr[pnum].AnimInfo._pAnimFrame == 7) + || (plr[pnum]._pWFrames != 8 && plr[pnum].AnimInfo._pAnimFrame == 4)) { PlaySfxLoc(PS_WALK1, plr[pnum].position.tile.x, plr[pnum].position.tile.y); } } //"Jog" in town which works by doubling movement speed and skipping every other animation frame if (currlevel == 0 && sgGameInitInfo.bRunInTown) { - if (plr[pnum]._pAnimFrame % 2 == 0) { - plr[pnum]._pAnimFrame++; + if (plr[pnum].AnimInfo._pAnimFrame % 2 == 0) { + plr[pnum].AnimInfo._pAnimFrame++; plr[pnum].actionFrame++; } - if (plr[pnum]._pAnimFrame >= plr[pnum]._pWFrames) { - plr[pnum]._pAnimFrame = 0; + if (plr[pnum].AnimInfo._pAnimFrame >= plr[pnum]._pWFrames) { + plr[pnum].AnimInfo._pAnimFrame = 0; } } @@ -2799,24 +2799,24 @@ bool PM_DoAttack(int pnum) app_fatal("PM_DoAttack: illegal player %d", pnum); } - frame = plr[pnum]._pAnimFrame; + frame = plr[pnum].AnimInfo._pAnimFrame; if (plr[pnum]._pIFlags & ISPL_QUICKATTACK && frame == 1) { - plr[pnum]._pAnimFrame++; + plr[pnum].AnimInfo._pAnimFrame++; } if (plr[pnum]._pIFlags & ISPL_FASTATTACK && (frame == 1 || frame == 3)) { - plr[pnum]._pAnimFrame++; + plr[pnum].AnimInfo._pAnimFrame++; } if (plr[pnum]._pIFlags & ISPL_FASTERATTACK && (frame == 1 || frame == 3 || frame == 5)) { - plr[pnum]._pAnimFrame++; + plr[pnum].AnimInfo._pAnimFrame++; } if (plr[pnum]._pIFlags & ISPL_FASTESTATTACK && (frame == 1 || frame == 4)) { - plr[pnum]._pAnimFrame += 2; + plr[pnum].AnimInfo._pAnimFrame += 2; } - if (plr[pnum]._pAnimFrame == plr[pnum]._pAFNum - 1) { + if (plr[pnum].AnimInfo._pAnimFrame == plr[pnum]._pAFNum - 1) { PlaySfxLoc(PS_SWING, plr[pnum].position.tile.x, plr[pnum].position.tile.y); } - if (plr[pnum]._pAnimFrame == plr[pnum]._pAFNum) { + if (plr[pnum].AnimInfo._pAnimFrame == plr[pnum]._pAFNum) { dx = plr[pnum].position.tile.x + offset_x[plr[pnum]._pdir]; dy = plr[pnum].position.tile.y + offset_y[plr[pnum]._pdir]; @@ -2893,7 +2893,7 @@ bool PM_DoAttack(int pnum) } } - if (plr[pnum]._pAnimFrame == plr[pnum]._pAFrames) { + if (plr[pnum].AnimInfo._pAnimFrame == plr[pnum]._pAFrames) { StartStand(pnum, plr[pnum]._pdir); ClearPlrPVars(pnum); return true; @@ -2910,20 +2910,20 @@ bool PM_DoRangeAttack(int pnum) } if (!gbIsHellfire) { - origFrame = plr[pnum]._pAnimFrame; + origFrame = plr[pnum].AnimInfo._pAnimFrame; if (plr[pnum]._pIFlags & ISPL_QUICKATTACK && origFrame == 1) { - plr[pnum]._pAnimFrame++; + plr[pnum].AnimInfo._pAnimFrame++; } if (plr[pnum]._pIFlags & ISPL_FASTATTACK && (origFrame == 1 || origFrame == 3)) { - plr[pnum]._pAnimFrame++; + plr[pnum].AnimInfo._pAnimFrame++; } } int arrows = 0; - if (plr[pnum]._pAnimFrame == plr[pnum]._pAFNum) { + if (plr[pnum].AnimInfo._pAnimFrame == plr[pnum]._pAFNum) { arrows = 1; } - if ((plr[pnum]._pIFlags & ISPL_MULT_ARROWS) != 0 && plr[pnum]._pAnimFrame == plr[pnum]._pAFNum + 2) { + if ((plr[pnum]._pIFlags & ISPL_MULT_ARROWS) != 0 && plr[pnum].AnimInfo._pAnimFrame == plr[pnum]._pAFNum + 2) { arrows = 2; } @@ -2976,7 +2976,7 @@ bool PM_DoRangeAttack(int pnum) } } - if (plr[pnum]._pAnimFrame >= plr[pnum]._pAFrames) { + if (plr[pnum].AnimInfo._pAnimFrame >= plr[pnum]._pAFrames) { StartStand(pnum, plr[pnum]._pdir); ClearPlrPVars(pnum); return true; @@ -3025,11 +3025,11 @@ bool PM_DoBlock(int pnum) app_fatal("PM_DoBlock: illegal player %d", pnum); } - if (plr[pnum]._pIFlags & ISPL_FASTBLOCK && plr[pnum]._pAnimFrame != 1) { - plr[pnum]._pAnimFrame = plr[pnum]._pBFrames; + if (plr[pnum]._pIFlags & ISPL_FASTBLOCK && plr[pnum].AnimInfo._pAnimFrame != 1) { + plr[pnum].AnimInfo._pAnimFrame = plr[pnum]._pBFrames; } - if (plr[pnum]._pAnimFrame >= plr[pnum]._pBFrames) { + if (plr[pnum].AnimInfo._pAnimFrame >= plr[pnum]._pBFrames) { StartStand(pnum, plr[pnum]._pdir); ClearPlrPVars(pnum); @@ -3121,7 +3121,7 @@ bool PM_DoSpell(int pnum) ClearPlrPVars(pnum); return true; } - } else if (plr[pnum]._pAnimFrame == plr[pnum]._pSFrames) { + } else if (plr[pnum].AnimInfo._pAnimFrame == plr[pnum]._pSFrames) { StartStand(pnum, plr[pnum]._pdir); ClearPlrPVars(pnum); return true; @@ -3138,18 +3138,18 @@ bool PM_DoGotHit(int pnum) app_fatal("PM_DoGotHit: illegal player %d", pnum); } - frame = plr[pnum]._pAnimFrame; + frame = plr[pnum].AnimInfo._pAnimFrame; if (plr[pnum]._pIFlags & ISPL_FASTRECOVER && frame == 3) { - plr[pnum]._pAnimFrame++; + plr[pnum].AnimInfo._pAnimFrame++; } if (plr[pnum]._pIFlags & ISPL_FASTERRECOVER && (frame == 3 || frame == 5)) { - plr[pnum]._pAnimFrame++; + plr[pnum].AnimInfo._pAnimFrame++; } if (plr[pnum]._pIFlags & ISPL_FASTESTRECOVER && (frame == 1 || frame == 3 || frame == 5)) { - plr[pnum]._pAnimFrame++; + plr[pnum].AnimInfo._pAnimFrame++; } - if (plr[pnum]._pAnimFrame >= plr[pnum]._pHFrames) { + if (plr[pnum].AnimInfo._pAnimFrame >= plr[pnum]._pHFrames) { StartStand(pnum, plr[pnum]._pdir); ClearPlrPVars(pnum); if (GenerateRnd(4) != 0) { @@ -3179,8 +3179,8 @@ bool PM_DoDeath(int pnum) } } - plr[pnum]._pAnimDelay = 10000; - plr[pnum]._pAnimFrame = plr[pnum]._pAnimLen; + plr[pnum].AnimInfo._pAnimDelay = 10000; + plr[pnum].AnimInfo._pAnimFrame = plr[pnum].AnimInfo._pAnimLen; dFlags[plr[pnum].position.tile.x][plr[pnum].position.tile.y] |= BFLAG_DEAD_PLAYER; } @@ -3437,7 +3437,7 @@ void CheckNewPath(int pnum) return; } - if (plr[pnum]._pmode == PM_ATTACK && plr[pnum]._pAnimFrame > plr[myplr]._pAFNum) { + if (plr[pnum]._pmode == PM_ATTACK && plr[pnum].AnimInfo._pAnimFrame > plr[myplr]._pAFNum) { if (plr[pnum].destAction == ACTION_ATTACK) { d = GetDirection(plr[pnum].position.future, { plr[pnum].destParam1, plr[pnum].destParam2 }); StartAttack(pnum, d); @@ -3476,7 +3476,7 @@ void CheckNewPath(int pnum) } } - if (plr[pnum]._pmode == PM_RATTACK && plr[pnum]._pAnimFrame > plr[myplr]._pAFNum) { + if (plr[pnum]._pmode == PM_RATTACK && plr[pnum].AnimInfo._pAnimFrame > plr[myplr]._pAFNum) { if (plr[pnum].destAction == ACTION_RATTACK) { d = GetDirection(plr[pnum].position.tile, { plr[pnum].destParam1, plr[pnum].destParam2 }); StartRangeAttack(pnum, d, plr[pnum].destParam1, plr[pnum].destParam2); @@ -3494,7 +3494,7 @@ void CheckNewPath(int pnum) } } - if (plr[pnum]._pmode == PM_SPELL && plr[pnum]._pAnimFrame > plr[pnum]._pSFNum) { + if (plr[pnum]._pmode == PM_SPELL && plr[pnum].AnimInfo._pAnimFrame > plr[pnum]._pSFNum) { if (plr[pnum].destAction == ACTION_SPELL) { d = GetDirection(plr[pnum].position.tile, { plr[pnum].destParam1, plr[pnum].destParam2 }); StartSpell(pnum, d, plr[pnum].destParam1, plr[pnum].destParam2); @@ -3714,14 +3714,14 @@ void ProcessPlayers() void ProcessPlayerAnimation(int pnum) { - plr[pnum]._pAnimCnt++; - plr[pnum]._pAnimGameTicksSinceSequenceStarted++; - if (plr[pnum]._pAnimCnt > plr[pnum]._pAnimDelay) { - plr[pnum]._pAnimCnt = 0; - plr[pnum]._pAnimFrame++; - if (plr[pnum]._pAnimFrame > plr[pnum]._pAnimLen) { - plr[pnum]._pAnimFrame = 1; - plr[pnum]._pAnimGameTicksSinceSequenceStarted = 0; + plr[pnum].AnimInfo._pAnimCnt++; + plr[pnum].AnimInfo._pAnimGameTicksSinceSequenceStarted++; + if (plr[pnum].AnimInfo._pAnimCnt > plr[pnum].AnimInfo._pAnimDelay) { + plr[pnum].AnimInfo._pAnimCnt = 0; + plr[pnum].AnimInfo._pAnimFrame++; + if (plr[pnum].AnimInfo._pAnimFrame > plr[pnum].AnimInfo._pAnimLen) { + plr[pnum].AnimInfo._pAnimFrame = 1; + plr[pnum].AnimInfo._pAnimGameTicksSinceSequenceStarted = 0; } } } @@ -3732,22 +3732,22 @@ int GetFrameToUseForPlayerRendering(const PlayerStruct *pPlayer) // - 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->_pAnimRelevantAnimationFramesForDistributing; + int relevantAnimationFramesForDistributing = pPlayer->AnimInfo._pAnimRelevantAnimationFramesForDistributing; if (relevantAnimationFramesForDistributing <= 0) - return pPlayer->_pAnimFrame; + return pPlayer->AnimInfo._pAnimFrame; - if (pPlayer->_pAnimFrame > relevantAnimationFramesForDistributing) - return pPlayer->_pAnimFrame; + if (pPlayer->AnimInfo._pAnimFrame > relevantAnimationFramesForDistributing) + return pPlayer->AnimInfo._pAnimFrame; - assert(pPlayer->_pAnimGameTicksSinceSequenceStarted >= 0); + assert(pPlayer->AnimInfo._pAnimGameTicksSinceSequenceStarted >= 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->_pAnimGameTicksSinceSequenceStarted; + float totalGameTicksForCurrentAnimationSequence = progressToNextGameTick + (float)pPlayer->AnimInfo._pAnimGameTicksSinceSequenceStarted; // 1 added for rounding reasons. float to int cast always truncate. - int absoluteAnimationFrame = 1 + (int)(totalGameTicksForCurrentAnimationSequence * pPlayer->_pAnimGameTickModifier); + int absoluteAnimationFrame = 1 + (int)(totalGameTicksForCurrentAnimationSequence * pPlayer->AnimInfo._pAnimGameTickModifier); 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)) { @@ -3966,21 +3966,21 @@ void SyncPlrAnim(int pnum) dir = plr[pnum]._pdir; switch (plr[pnum]._pmode) { case PM_STAND: - plr[pnum]._pAnimData = plr[pnum]._pNAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pNAnim[dir]; break; case PM_WALK: case PM_WALK2: case PM_WALK3: - plr[pnum]._pAnimData = plr[pnum]._pWAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pWAnim[dir]; break; case PM_ATTACK: - plr[pnum]._pAnimData = plr[pnum]._pAAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pAAnim[dir]; break; case PM_RATTACK: - plr[pnum]._pAnimData = plr[pnum]._pAAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pAAnim[dir]; break; case PM_BLOCK: - plr[pnum]._pAnimData = plr[pnum]._pBAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pBAnim[dir]; break; case PM_SPELL: if (pnum == myplr) @@ -3988,23 +3988,23 @@ void SyncPlrAnim(int pnum) else sType = STYPE_FIRE; if (sType == STYPE_FIRE) - plr[pnum]._pAnimData = plr[pnum]._pFAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pFAnim[dir]; if (sType == STYPE_LIGHTNING) - plr[pnum]._pAnimData = plr[pnum]._pLAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pLAnim[dir]; if (sType == STYPE_MAGIC) - plr[pnum]._pAnimData = plr[pnum]._pTAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pTAnim[dir]; break; case PM_GOTHIT: - plr[pnum]._pAnimData = plr[pnum]._pHAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pHAnim[dir]; break; case PM_NEWLVL: - plr[pnum]._pAnimData = plr[pnum]._pNAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pNAnim[dir]; break; case PM_DEATH: - plr[pnum]._pAnimData = plr[pnum]._pDAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pDAnim[dir]; break; case PM_QUIT: - plr[pnum]._pAnimData = plr[pnum]._pNAnim[dir]; + plr[pnum].AnimInfo._pAnimData = plr[pnum]._pNAnim[dir]; break; default: app_fatal("SyncPlrAnim"); diff --git a/Source/player.h b/Source/player.h index 5b4b26eac..d51051aa5 100644 --- a/Source/player.h +++ b/Source/player.h @@ -16,6 +16,7 @@ #include "path.h" #include "interfac.h" #include "utils/enum_traits.h" +#include "engine/animationinfo.h" namespace devilution { @@ -160,24 +161,11 @@ struct PlayerStruct { ActorPosition position; direction _pdir; // Direction faced by player (direction enum) int _pgfxnum; // Bitmask indicating what variant of the sprite the player is using. Lower byte define weapon (anim_weapon_id) and higher values define armour (starting with anim_armor_id) - uint8_t *_pAnimData; - int _pAnimDelay; // Tick length of each frame in the current animation - int _pAnimCnt; // Increases by one each game tick, counting how close we are to _pAnimDelay - int _pAnimLen; // Number of frames in current animation - int _pAnimFrame; // Current frame of animation. - int _pAnimWidth; - /* - * @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). - */ - float _pAnimGameTickModifier; /* - * @brief Number of GameTicks after the current animation sequence started + * @brief Contains Information for current Animation */ - int _pAnimGameTicksSinceSequenceStarted; - /* - * @brief Animation Frames that will be adjusted for the skipped Frames/GameTicks - */ - int _pAnimRelevantAnimationFramesForDistributing; + AnimationInfo AnimInfo; + int _pAnimWidth; int _plid; int _pvid; spell_id _pSpell; diff --git a/Source/scrollrt.cpp b/Source/scrollrt.cpp index adccc8af1..245052217 100644 --- a/Source/scrollrt.cpp +++ b/Source/scrollrt.cpp @@ -416,7 +416,7 @@ static void DrawPlayer(const CelOutputBuffer &out, int pnum, int x, int y, int p PlayerStruct *pPlayer = &plr[pnum]; - BYTE *pCelBuff = pPlayer->_pAnimData; + BYTE *pCelBuff = pPlayer->AnimInfo._pAnimData; int nCel = GetFrameToUseForPlayerRendering(pPlayer); int nWidth = pPlayer->_pAnimWidth; diff --git a/test/animationinformation_test.cpp b/test/animationinfo_test.cpp similarity index 89% rename from test/animationinformation_test.cpp rename to test/animationinfo_test.cpp index 90c44556f..d7d89304e 100644 --- a/test/animationinformation_test.cpp +++ b/test/animationinfo_test.cpp @@ -1,7 +1,7 @@ #include #include "nthread.h" -#include "player.h" +#include "engine/animationinfo.h" using namespace devilution; @@ -53,8 +53,8 @@ struct RenderingData : TestData { void RunAnimationTest(int numFrames, int delay, AnimationDistributionParams params, int numSkippedFrames, int distributeFramesBeforeFrame, const std::vector &vecTestData) { const int pnum = 0; - PlayerStruct *pPlayer = &plr[pnum]; - NewPlrAnim(pnum, nullptr, numFrames, delay, 128, params, numSkippedFrames, distributeFramesBeforeFrame); + AnimationInfo animInfo; + animInfo.SetNewAnimation(nullptr, numFrames, delay, params, numSkippedFrames, distributeFramesBeforeFrame); int currentGameTick = 0; for (TestData *x : vecTestData) { @@ -62,10 +62,10 @@ void RunAnimationTest(int numFrames, int delay, AnimationDistributionParams para if (gameTickData != nullptr) { currentGameTick += 1; if (gameTickData->_FramesToSkip != 0) - pPlayer->_pAnimFrame += gameTickData->_FramesToSkip; + pPlayer->AnimInfo._pAnimFrame += gameTickData->_FramesToSkip; ProcessPlayerAnimation(pnum); - EXPECT_EQ(pPlayer->_pAnimFrame, gameTickData->_ExpectedAnimationFrame); - EXPECT_EQ(pPlayer->_pAnimCnt, gameTickData->_ExpectedAnimationCnt); + EXPECT_EQ(pPlayer->AnimInfo._pAnimFrame, gameTickData->_ExpectedAnimationFrame); + EXPECT_EQ(pPlayer->AnimInfo._pAnimCnt, gameTickData->_ExpectedAnimationCnt); } auto renderingData = dynamic_cast(x); @@ -74,8 +74,8 @@ void RunAnimationTest(int numFrames, int delay, AnimationDistributionParams para EXPECT_EQ(GetFrameToUseForPlayerRendering(pPlayer), renderingData->_ExpectedRenderingFrame) << std::fixed << std::setprecision(2) << "ProgressToNextGameTick: " << renderingData->_fProgressToNextGameTick - << " AnimFrame: " << pPlayer->_pAnimFrame - << " AnimCnt: " << pPlayer->_pAnimCnt + << " AnimFrame: " << pPlayer->AnimInfo._pAnimFrame + << " AnimCnt: " << pPlayer->AnimInfo._pAnimCnt << " GameTick: " << currentGameTick; } } @@ -84,11 +84,11 @@ void RunAnimationTest(int numFrames, int delay, AnimationDistributionParams para } } -TEST(AnimationInformation, AttackSwordWarrior) // ProcessAnimationPending should be considered by distribution logic +TEST(AnimationInfo, AttackSwordWarrior) // ProcessAnimationPending should be considered by distribution logic { RunAnimationTest(16, 0, AnimationDistributionParams::ProcessAnimationPending, 0, 9, { - // ProcessPlayer directly after StartAttack (in same GameTick). So we don't see any rendering before. + // ProcessAnimation directly after StartAttack (in same GameTick). So we don't see any rendering before. new GameTickData(2, 0), new RenderingData(0.0f, 1), new RenderingData(0.3f, 1), @@ -146,11 +146,11 @@ TEST(AnimationInformation, AttackSwordWarrior) // ProcessAnimationPending should }); } -TEST(AnimationInformation, AttackSwordWarriorWithFastestAttack) // Skipped frames and ProcessAnimationPending should be considered by distribution logic +TEST(AnimationInfo, AttackSwordWarriorWithFastestAttack) // Skipped frames and ProcessAnimationPending should be considered by distribution logic { RunAnimationTest(16, 0, AnimationDistributionParams::ProcessAnimationPending, 2, 9, { - // ProcessPlayer directly after StartAttack (in same GameTick). So we don't see any rendering before. + // ProcessAnimation directly after StartAttack (in same GameTick). So we don't see any rendering before. new GameTickData(2, 0), new RenderingData(0.0f, 1), new RenderingData(0.3f, 1), @@ -198,7 +198,7 @@ TEST(AnimationInformation, AttackSwordWarriorWithFastestAttack) // Skipped frame }); } -TEST(AnimationInformation, BlockingWarriorNormal) // Ignored delay for last Frame should be considered by distribution logic +TEST(AnimationInfo, BlockingWarriorNormal) // Ignored delay for last Frame should be considered by distribution logic { RunAnimationTest(2, 2, AnimationDistributionParams::SkipsDelayOfLastFrame, 0, 0, { @@ -225,7 +225,7 @@ TEST(AnimationInformation, BlockingWarriorNormal) // Ignored delay for last Fram }); } -TEST(AnimationInformation, BlockingSorcererWithFastBlock) // Skipped frames and ignored delay for last Frame should be considered by distribution logic +TEST(AnimationInfo, BlockingSorcererWithFastBlock) // Skipped frames and ignored delay for last Frame should be considered by distribution logic { RunAnimationTest(6, 2, AnimationDistributionParams::SkipsDelayOfLastFrame, 4, 0, { @@ -252,7 +252,7 @@ TEST(AnimationInformation, BlockingSorcererWithFastBlock) // Skipped frames and }); } -TEST(AnimationInformation, HitRecoverySorcererZenMode) // Skipped frames and ignored delay for last Frame should be considered by distribution logic +TEST(AnimationInfo, HitRecoverySorcererZenMode) // Skipped frames and ignored delay for last Frame should be considered by distribution logic { RunAnimationTest(8, 0, AnimationDistributionParams::None, 4, 0, { @@ -278,7 +278,7 @@ TEST(AnimationInformation, HitRecoverySorcererZenMode) // Skipped frames and ign // 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 +TEST(AnimationInfo, Stand) // Distribution Logic shouldn't change anything here { RunAnimationTest(10, 3, AnimationDistributionParams::None, 0, 0, { diff --git a/test/player_test.cpp b/test/player_test.cpp index 3162605b1..0abc3b1b6 100644 --- a/test/player_test.cpp +++ b/test/player_test.cpp @@ -11,7 +11,7 @@ extern bool PM_DoGotHit(int pnum); int RunBlockTest(int frames, int flags) { int pnum = 0; - plr[pnum]._pAnimFrame = 1; + plr[pnum].AnimInfo._pAnimFrame = 1; plr[pnum]._pHFrames = frames; plr[pnum].actionFrame = 1; plr[pnum]._pIFlags = flags; @@ -23,7 +23,7 @@ int RunBlockTest(int frames, int flags) PM_DoGotHit(pnum); if (plr[pnum]._pmode != PM_GOTHIT) break; - plr[pnum]._pAnimFrame++; + plr[pnum].AnimInfo._pAnimFrame++; } return i; diff --git a/test/writehero_test.cpp b/test/writehero_test.cpp index b78d83ff8..0c1e0fb3d 100644 --- a/test/writehero_test.cpp +++ b/test/writehero_test.cpp @@ -292,10 +292,10 @@ static void AssertPlayer(PlayerStruct *pPlayer) ASSERT_EQ(pPlayer->_pmode, 0); ASSERT_EQ(Count8(pPlayer->walkpath, MAX_PATH_LENGTH), 25); ASSERT_EQ(pPlayer->_pgfxnum, 36); - ASSERT_EQ(pPlayer->_pAnimDelay, 3); - ASSERT_EQ(pPlayer->_pAnimCnt, 1); - ASSERT_EQ(pPlayer->_pAnimLen, 20); - ASSERT_EQ(pPlayer->_pAnimFrame, 1); + ASSERT_EQ(pPlayer->AnimInfo._pAnimDelay, 3); + ASSERT_EQ(pPlayer->AnimInfo._pAnimCnt, 1); + ASSERT_EQ(pPlayer->AnimInfo._pAnimLen, 20); + ASSERT_EQ(pPlayer->AnimInfo._pAnimFrame, 1); ASSERT_EQ(pPlayer->_pAnimWidth, 96); ASSERT_EQ(pPlayer->_pSpell, -1); ASSERT_EQ(pPlayer->_pSplType, 4);