From a66ca446950bceb25ac31ee268dfc8e2ec9259ce Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Wed, 23 Mar 2022 10:00:48 +0000 Subject: [PATCH] Zero-based frame indexing Index frames starting at 0 instead of 1. --- Source/control.cpp | 18 +- Source/cursor.cpp | 29 ++- Source/cursor.h | 2 +- Source/dead.cpp | 4 +- Source/doom.cpp | 2 +- Source/engine/animationinfo.cpp | 43 ++-- Source/engine/cel_header.hpp | 29 ++- Source/engine/cel_sprite.hpp | 2 +- Source/engine/render/cel_render.hpp | 2 +- Source/engine/render/cl2_render.cpp | 12 +- Source/engine/render/cl2_render.hpp | 4 +- Source/engine/render/text_render.cpp | 2 +- Source/error.cpp | 16 +- Source/gmenu.cpp | 12 +- Source/interfac.cpp | 2 +- Source/inv.cpp | 22 +-- Source/items.cpp | 26 +-- Source/loadsave.cpp | 12 +- Source/minitext.cpp | 2 +- Source/missiles.h | 2 +- Source/monster.cpp | 80 ++++---- Source/msg.cpp | 2 +- Source/multi.cpp | 2 +- Source/panels/charpanel.cpp | 8 +- Source/panels/spell_book.cpp | 8 +- Source/panels/spell_icons.cpp | 58 +++--- Source/panels/spell_list.cpp | 2 +- Source/player.cpp | 47 ++--- Source/player.h | 8 +- Source/qol/stash.cpp | 2 +- Source/quests.cpp | 2 +- Source/scrollrt.cpp | 46 ++--- Source/stores.cpp | 14 +- Source/towners.cpp | 98 +++++----- test/animationinfo_test.cpp | 282 +++++++++++++-------------- test/writehero_test.cpp | 2 +- 36 files changed, 456 insertions(+), 448 deletions(-) diff --git a/Source/control.cpp b/Source/control.cpp index 3bf5adf20..82961864b 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -528,16 +528,16 @@ void InitControlPan() LoadCharPanel(); LoadSpellIcons(); - CelDrawUnsafeTo(*pBtmBuff, { 0, (PANEL_HEIGHT + 16) - 1 }, LoadCel("CtrlPan\\Panel8.CEL", PANEL_WIDTH), 1); + CelDrawUnsafeTo(*pBtmBuff, { 0, (PANEL_HEIGHT + 16) - 1 }, LoadCel("CtrlPan\\Panel8.CEL", PANEL_WIDTH), 0); { const Point bulbsPosition { 0, 87 }; const OwnedCelSprite statusPanel = LoadCel("CtrlPan\\P8Bulbs.CEL", 88); - CelDrawUnsafeTo(*pLifeBuff, bulbsPosition, statusPanel, 1); - CelDrawUnsafeTo(*pManaBuff, bulbsPosition, statusPanel, 2); + CelDrawUnsafeTo(*pLifeBuff, bulbsPosition, statusPanel, 0); + CelDrawUnsafeTo(*pManaBuff, bulbsPosition, statusPanel, 1); } talkflag = false; if (IsChatAvailable()) { - CelDrawUnsafeTo(*pBtmBuff, { 0, (PANEL_HEIGHT + 16) * 2 - 1 }, LoadCel("CtrlPan\\TalkPanl.CEL", PANEL_WIDTH), 1); + CelDrawUnsafeTo(*pBtmBuff, { 0, (PANEL_HEIGHT + 16) * 2 - 1 }, LoadCel("CtrlPan\\TalkPanl.CEL", PANEL_WIDTH), 0); multiButtons = LoadCel("CtrlPan\\P8But2.CEL", 33); talkButtons = LoadCel("CtrlPan\\TalkButt.CEL", 61); sgbPlrTalkTbl = 0; @@ -596,16 +596,16 @@ void DrawCtrlBtns(const Surface &out) DrawPanelBox(out, MakeSdlRect(PanBtnPos[i].x, PanBtnPos[i].y + 16, 71, 20), { PanBtnPos[i].x + PANEL_X, PanBtnPos[i].y + PANEL_Y }); } else { Point position { PanBtnPos[i].x + PANEL_X, PanBtnPos[i].y + PANEL_Y + 18 }; - CelDrawTo(out, position, *pPanelButtons, i + 1); + CelDrawTo(out, position, *pPanelButtons, i); DrawArt(out, position + Displacement { 4, -18 }, &PanelButtonDown, i); } } if (PanelButtonIndex == 8) { - CelDrawTo(out, { 87 + PANEL_X, 122 + PANEL_Y }, *multiButtons, PanelButtons[6] ? 2 : 1); + CelDrawTo(out, { 87 + PANEL_X, 122 + PANEL_Y }, *multiButtons, PanelButtons[6] ? 1 : 0); if (gbFriendlyMode) - CelDrawTo(out, { 527 + PANEL_X, 122 + PANEL_Y }, *multiButtons, PanelButtons[7] ? 4 : 3); + CelDrawTo(out, { 527 + PANEL_X, 122 + PANEL_Y }, *multiButtons, PanelButtons[7] ? 3 : 2); else - CelDrawTo(out, { 527 + PANEL_X, 122 + PANEL_Y }, *multiButtons, PanelButtons[7] ? 6 : 5); + CelDrawTo(out, { 527 + PANEL_X, 122 + PANEL_Y }, *multiButtons, PanelButtons[7] ? 5 : 4); } } @@ -1031,7 +1031,7 @@ void DrawGoldSplit(const Surface &out, int amount) { const int dialogX = 30; - CelDrawTo(out, GetPanelPosition(UiPanels::Inventory, { dialogX, 178 }), *pGBoxBuff, 1); + CelDrawTo(out, GetPanelPosition(UiPanels::Inventory, { dialogX, 178 }), *pGBoxBuff, 0); const std::string description = fmt::format( ngettext( diff --git a/Source/cursor.cpp b/Source/cursor.cpp index 43fb78b31..cc4707a41 100644 --- a/Source/cursor.cpp +++ b/Source/cursor.cpp @@ -33,13 +33,12 @@ namespace { /** Cursor images CEL */ std::optional pCursCels; std::optional pCursCels2; -constexpr uint16_t InvItems1Size = 180; /** Maps from objcurs.cel frame number to frame width. */ const uint16_t InvItemWidth1[] = { // clang-format off // Cursors - 0, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 23, + 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 23, // Items 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, @@ -60,7 +59,6 @@ const uint16_t InvItemWidth1[] = { 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, }; const uint16_t InvItemWidth2[] = { - 0, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, @@ -69,12 +67,13 @@ const uint16_t InvItemWidth2[] = { 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28 // clang-format on }; +constexpr uint16_t InvItems1Size = sizeof(InvItemWidth1) / sizeof(InvItemWidth1[0]); /** Maps from objcurs.cel frame number to frame height. */ -const uint16_t InvItemHeight1[] = { +const uint16_t InvItemHeight1[InvItems1Size] = { // clang-format off // Cursors - 0, 29, 32, 32, 32, 32, 32, 32, 32, 32, 32, 35, + 29, 32, 32, 32, 32, 32, 32, 32, 32, 32, 35, // Items 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, @@ -95,7 +94,6 @@ const uint16_t InvItemHeight1[] = { 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, }; const uint16_t InvItemHeight2[] = { - 0, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, @@ -149,26 +147,27 @@ void FreeCursor() ClearCursor(); } -const OwnedCelSprite &GetInvItemSprite(int i) +const OwnedCelSprite &GetInvItemSprite(int cursId) { - return i < InvItems1Size ? *pCursCels : *pCursCels2; + return cursId <= InvItems1Size ? *pCursCels : *pCursCels2; } -int GetInvItemFrame(int i) +int GetInvItemFrame(int cursId) { - return i < InvItems1Size ? i : i - (InvItems1Size - 1); + return cursId <= InvItems1Size ? cursId - 1 : cursId - InvItems1Size - 1; } Size GetInvItemSize(int cursId) { - if (cursId >= InvItems1Size) - return { InvItemWidth2[cursId - (InvItems1Size - 1)], InvItemHeight2[cursId - (InvItems1Size - 1)] }; - return { InvItemWidth1[cursId], InvItemHeight1[cursId] }; + const int i = cursId - 1; + if (i >= InvItems1Size) + return { InvItemWidth2[i - InvItems1Size], InvItemHeight2[i - InvItems1Size] }; + return { InvItemWidth1[i], InvItemHeight1[i] }; } void SetICursor(int cursId) { - icursSize = GetInvItemSize(cursId); + icursSize = cursId == CURSOR_NONE ? Size { 0, 0 } : GetInvItemSize(cursId); icursSize28 = icursSize / 28; } @@ -183,7 +182,7 @@ void NewCursor(int cursId) MyPlayer->HoldItem._itype = ItemType::None; } pcurs = cursId; - cursSize = GetInvItemSize(cursId); + cursSize = cursId == CURSOR_NONE ? Size { 0, 0 } : GetInvItemSize(cursId); SetICursor(cursId); if (IsHardwareCursorEnabled() && ControlDevice == ControlTypes::KeyboardAndMouse) { diff --git a/Source/cursor.h b/Source/cursor.h index 175f2225a..6d427ea66 100644 --- a/Source/cursor.h +++ b/Source/cursor.h @@ -65,7 +65,7 @@ void CelDrawCursor(const Surface &out, Point position, int cursId); const OwnedCelSprite &GetInvItemSprite(int i); /** Returns the CEL frame index for the given inventory index. */ -int GetInvItemFrame(int i); +int GetInvItemFrame(int cursId); /** Returns the width and height for an inventory index. */ Size GetInvItemSize(int cursId); diff --git a/Source/dead.cpp b/Source/dead.cpp index d71d95a21..ffa41d676 100644 --- a/Source/dead.cpp +++ b/Source/dead.cpp @@ -20,7 +20,7 @@ void InitDeadAnimationFromMonster(Corpse &corpse, const CMonster &mon) { const auto &animData = mon.GetAnimData(MonsterGraphic::Death); memcpy(&corpse.data[0], &animData.CelSpritesForDirections[0], sizeof(animData.CelSpritesForDirections[0]) * animData.CelSpritesForDirections.size()); - corpse.frame = animData.Frames; + corpse.frame = animData.Frames - 1; corpse.width = animData.Width; } } // namespace @@ -48,7 +48,7 @@ void InitCorpses() for (auto &corpse : Corpses[nd].data) corpse = MissileSpriteData[MFILE_SHATTER1].GetFirstFrame(); - Corpses[nd].frame = 12; + Corpses[nd].frame = 11; Corpses[nd].width = 128; Corpses[nd].translationPaletteIndex = 0; nd++; diff --git a/Source/doom.cpp b/Source/doom.cpp index 1ce4118b8..ce7eee0b9 100644 --- a/Source/doom.cpp +++ b/Source/doom.cpp @@ -37,7 +37,7 @@ void doom_draw(const Surface &out) return; } - CelDrawTo(out, { PANEL_X, PANEL_Y - 1 }, *DoomCel, 1); + CelDrawTo(out, { PANEL_X, PANEL_Y - 1 }, *DoomCel, 0); } } // namespace devilution diff --git a/Source/engine/animationinfo.cpp b/Source/engine/animationinfo.cpp index 1356e43e3..8f6994374 100644 --- a/Source/engine/animationinfo.cpp +++ b/Source/engine/animationinfo.cpp @@ -19,9 +19,9 @@ int AnimationInfo::GetFrameToUseForRendering() const // 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) if (RelevantFramesForDistributing <= 0) - return std::max(1, CurrentFrame); + return std::max(0, CurrentFrame); - if (CurrentFrame > RelevantFramesForDistributing) + if (CurrentFrame >= RelevantFramesForDistributing) return CurrentFrame; float ticksSinceSequenceStarted = TicksSinceSequenceStarted; @@ -33,26 +33,25 @@ int AnimationInfo::GetFrameToUseForRendering() const // we don't use the processed game ticks alone but also the fraction of the next game tick (if a rendering happens between game ticks). This helps to smooth the animations. float totalTicksForCurrentAnimationSequence = GetProgressToNextGameTick() + ticksSinceSequenceStarted; - // 1 added for rounding reasons. float to int cast always truncate. - int absoluteAnimationFrame = 1 + static_cast(totalTicksForCurrentAnimationSequence * TickModifier); + int absoluteAnimationFrame = static_cast(totalTicksForCurrentAnimationSequence * TickModifier); 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) { + if (absoluteAnimationFrame < 0) { // We still display the remains of the previous Animation absoluteAnimationFrame = NumberOfFrames + absoluteAnimationFrame; } - } else if (absoluteAnimationFrame > RelevantFramesForDistributing) { + } 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)) { + if (absoluteAnimationFrame >= (RelevantFramesForDistributing + 1)) { // we should never have +2 frames even if next game tick is due Log("GetFrameToUseForRendering: Calculated an invalid Animation Frame (Calculated {} MaxFrame {})", absoluteAnimationFrame, RelevantFramesForDistributing); } - return RelevantFramesForDistributing; + return RelevantFramesForDistributing - 1; } - if (absoluteAnimationFrame <= 0) { + if (absoluteAnimationFrame < 0) { Log("GetFrameToUseForRendering: Calculated an invalid Animation Frame (Calculated {})", absoluteAnimationFrame); - return 1; + return 0; } return absoluteAnimationFrame; } @@ -65,7 +64,7 @@ float AnimationInfo::GetAnimationProgress() const if (RelevantFramesForDistributing <= 0) { // This logic is used if animation distribution is not active (see GetFrameToUseForRendering). // In this case the variables calculated with animation distribution are not initialized and we have to calculate them on the fly with the given information. - ticksSinceSequenceStarted = static_cast(((CurrentFrame - 1) * TicksPerFrame) + TickCounterOfCurrentFrame); + ticksSinceSequenceStarted = static_cast((CurrentFrame * TicksPerFrame) + TickCounterOfCurrentFrame); tickModifier = 1.0F / static_cast(TicksPerFrame); } @@ -77,10 +76,10 @@ float AnimationInfo::GetAnimationProgress() const void AnimationInfo::SetNewAnimation(std::optional celSprite, int numberOfFrames, int ticksPerFrame, AnimationDistributionFlags flags /*= AnimationDistributionFlags::None*/, int numSkippedFrames /*= 0*/, int distributeFramesBeforeFrame /*= 0*/, float previewShownGameTickFragments /*= 0.F*/) { - if ((flags & AnimationDistributionFlags::RepeatedAction) == AnimationDistributionFlags::RepeatedAction && distributeFramesBeforeFrame != 0 && NumberOfFrames == numberOfFrames && CurrentFrame >= distributeFramesBeforeFrame && CurrentFrame != NumberOfFrames) { + 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. // 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; + SkippedFramesFromPreviousAnimation = NumberOfFrames - CurrentFrame - 1; } else { SkippedFramesFromPreviousAnimation = 0; } @@ -92,7 +91,7 @@ void AnimationInfo::SetNewAnimation(std::optional celSprite, int numb this->celSprite = celSprite; NumberOfFrames = numberOfFrames; - CurrentFrame = 1 + numSkippedFrames; + CurrentFrame = numSkippedFrames; TickCounterOfCurrentFrame = 0; TicksPerFrame = ticksPerFrame; TicksSinceSequenceStarted = 0.F; @@ -173,9 +172,9 @@ void AnimationInfo::ChangeAnimationData(std::optional celSprite, int 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 if (numberOfFrames >= 1) - CurrentFrame = clamp(CurrentFrame, 1, numberOfFrames); + CurrentFrame = clamp(CurrentFrame, 0, numberOfFrames - 1); else - CurrentFrame = 0; + CurrentFrame = -1; NumberOfFrames = numberOfFrames; TicksPerFrame = ticksPerFrame; @@ -195,15 +194,15 @@ void AnimationInfo::ProcessAnimation(bool reverseAnimation /*= false*/, bool don if (TickCounterOfCurrentFrame >= TicksPerFrame) { TickCounterOfCurrentFrame = 0; if (reverseAnimation) { - CurrentFrame--; - if (CurrentFrame == 0) { - CurrentFrame = NumberOfFrames; + --CurrentFrame; + if (CurrentFrame == -1) { + CurrentFrame = NumberOfFrames - 1; TicksSinceSequenceStarted = 0.F; } } else { - CurrentFrame++; - if (CurrentFrame > NumberOfFrames) { - CurrentFrame = 1; + ++CurrentFrame; + if (CurrentFrame >= NumberOfFrames) { + CurrentFrame = 0; TicksSinceSequenceStarted = 0.F; } } diff --git a/Source/engine/cel_header.hpp b/Source/engine/cel_header.hpp index 9e8c0f899..06c0a381e 100644 --- a/Source/engine/cel_header.hpp +++ b/Source/engine/cel_header.hpp @@ -10,13 +10,24 @@ namespace devilution { -/** - * Returns the pointer to the start of the frame data (often a header). +/* + * When a CEL is a multi-direction animation, it begins with 8 offsets to the start of + * the animation for each direction. + * + * Fills the `out` array with a pointer to each direction. */ -inline byte *CelGetFrame(byte *data, int frame) +inline void CelGetDirectionFrames(const byte *data, const byte **out) { - const std::uint32_t begin = LoadLE32(&data[frame * sizeof(std::uint32_t)]); - return &data[begin]; + for (size_t i = 0; i < 8; ++i) { + out[i] = &data[LoadLE32(&data[i * 4])]; + } +} + +inline void CelGetDirectionFrames(byte *data, byte **out) +{ + for (size_t i = 0; i < 8; ++i) { + out[i] = &data[LoadLE32(&data[i * 4])]; + } } /** @@ -24,8 +35,8 @@ inline byte *CelGetFrame(byte *data, int frame) */ inline byte *CelGetFrame(byte *data, int frame, int *frameSize) { - const std::uint32_t begin = LoadLE32(&data[frame * sizeof(std::uint32_t)]); - *frameSize = static_cast(LoadLE32(&data[(frame + 1) * sizeof(std::uint32_t)]) - begin); + const std::uint32_t begin = LoadLE32(&data[(frame + 1) * sizeof(std::uint32_t)]); + *frameSize = static_cast(LoadLE32(&data[(frame + 2) * sizeof(std::uint32_t)]) - begin); return &data[begin]; } @@ -34,8 +45,8 @@ inline byte *CelGetFrame(byte *data, int frame, int *frameSize) */ inline const byte *CelGetFrame(const byte *data, int frame, int *frameSize) { - const std::uint32_t begin = LoadLE32(&data[frame * sizeof(std::uint32_t)]); - *frameSize = static_cast(LoadLE32(&data[(frame + 1) * sizeof(std::uint32_t)]) - begin); + const std::uint32_t begin = LoadLE32(&data[(frame + 1) * sizeof(std::uint32_t)]); + *frameSize = static_cast(LoadLE32(&data[(frame + 2) * sizeof(std::uint32_t)]) - begin); return &data[begin]; } diff --git a/Source/engine/cel_sprite.hpp b/Source/engine/cel_sprite.hpp index 201810c46..bcbbd52ce 100644 --- a/Source/engine/cel_sprite.hpp +++ b/Source/engine/cel_sprite.hpp @@ -40,7 +40,7 @@ public: return data_ptr_; } - [[nodiscard]] uint16_t Width(std::size_t frame = 1) const + [[nodiscard]] uint16_t Width(std::size_t frame = 0) const { return width_.HoldsPointer() ? width_.AsPointer()[frame] : width_.AsValue(); } diff --git a/Source/engine/render/cel_render.hpp b/Source/engine/render/cel_render.hpp index db6c1659c..c7fe9da1a 100644 --- a/Source/engine/render/cel_render.hpp +++ b/Source/engine/render/cel_render.hpp @@ -18,7 +18,7 @@ namespace devilution { * Returns a pair of X coordinates containing the start (inclusive) and end (exclusive) * of fully transparent columns in the sprite. */ -std::pair MeasureSolidHorizontalBounds(CelSprite cel, int frame = 1); +std::pair MeasureSolidHorizontalBounds(CelSprite cel, int frame = 0); /** * @brief Blit CEL sprite to the back buffer at the given coordinates diff --git a/Source/engine/render/cl2_render.cpp b/Source/engine/render/cl2_render.cpp index 1a4dea5aa..c36645665 100644 --- a/Source/engine/render/cl2_render.cpp +++ b/Source/engine/render/cl2_render.cpp @@ -745,11 +745,11 @@ void RenderCl2Outline(const Surface &out, Point position, const byte *src, std:: } // namespace -void Cl2ApplyTrans(byte *p, const std::array &ttbl, int nCel) +void Cl2ApplyTrans(byte *p, const std::array &ttbl, int numFrames) { assert(p != nullptr); - for (int i = 1; i <= nCel; i++) { + for (int i = 0; i < numFrames; ++i) { constexpr int FrameHeaderSize = 10; int nDataSize; byte *dst = CelGetFrame(p, i, &nDataSize) + FrameHeaderSize; @@ -780,7 +780,7 @@ void Cl2ApplyTrans(byte *p, const std::array &ttbl, int nCel) void Cl2Draw(const Surface &out, int sx, int sy, CelSprite cel, int frame) { - assert(frame > 0); + assert(frame >= 0); int nDataSize; const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); @@ -790,7 +790,7 @@ void Cl2Draw(const Surface &out, int sx, int sy, CelSprite cel, int frame) void Cl2DrawOutline(const Surface &out, uint8_t col, int sx, int sy, CelSprite cel, int frame) { - assert(frame > 0); + assert(frame >= 0); int nDataSize; const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); @@ -800,7 +800,7 @@ void Cl2DrawOutline(const Surface &out, uint8_t col, int sx, int sy, CelSprite c void Cl2DrawTRN(const Surface &out, int sx, int sy, CelSprite cel, int frame, uint8_t *trn) { - assert(frame > 0); + assert(frame >= 0); int nDataSize; const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); @@ -809,7 +809,7 @@ void Cl2DrawTRN(const Surface &out, int sx, int sy, CelSprite cel, int frame, ui void Cl2DrawLight(const Surface &out, int sx, int sy, CelSprite cel, int frame) { - assert(frame > 0); + assert(frame >= 0); int nDataSize; const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); diff --git a/Source/engine/render/cl2_render.hpp b/Source/engine/render/cl2_render.hpp index 48b382ac4..fbaa8d8f6 100644 --- a/Source/engine/render/cl2_render.hpp +++ b/Source/engine/render/cl2_render.hpp @@ -18,9 +18,9 @@ namespace devilution { * @brief Apply the color swaps to a CL2 sprite * @param p CL2 buffer * @param ttbl Palette translation table - * @param nCel Frame number in CL2 file + * @param numFrames Number of frames in the CL2 file */ -void Cl2ApplyTrans(byte *p, const std::array &ttbl, int nCel); +void Cl2ApplyTrans(byte *p, const std::array &ttbl, int numFrames); /** * @brief Blit CL2 sprite, to the back buffer at the given coordianates diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index a69c02ee9..01323229e 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -740,7 +740,7 @@ void DrawStringWithColors(const Surface &out, string_view fmt, DrawStringFormatA uint8_t PentSpn2Spin() { - return (SDL_GetTicks() / 50) % 8 + 1; + return (SDL_GetTicks() / 50) % 8; } } // namespace devilution diff --git a/Source/error.cpp b/Source/error.cpp index 971de189d..ea17f8fd4 100644 --- a/Source/error.cpp +++ b/Source/error.cpp @@ -143,21 +143,21 @@ void DrawDiabloMsg(const Surface &out) { int dialogStartY = ((gnScreenHeight - PANEL_HEIGHT) / 2) - (ErrorWindowHeight / 2) + 9; - CelDrawTo(out, { PANEL_X + 101, dialogStartY }, *pSTextSlidCels, 1); - CelDrawTo(out, { PANEL_X + 527, dialogStartY }, *pSTextSlidCels, 4); - CelDrawTo(out, { PANEL_X + 101, dialogStartY + ErrorWindowHeight - 6 }, *pSTextSlidCels, 2); - CelDrawTo(out, { PANEL_X + 527, dialogStartY + ErrorWindowHeight - 6 }, *pSTextSlidCels, 3); + CelDrawTo(out, { PANEL_X + 101, dialogStartY }, *pSTextSlidCels, 0); + CelDrawTo(out, { PANEL_X + 101, dialogStartY + ErrorWindowHeight - 6 }, *pSTextSlidCels, 1); + CelDrawTo(out, { PANEL_X + 527, dialogStartY + ErrorWindowHeight - 6 }, *pSTextSlidCels, 2); + CelDrawTo(out, { PANEL_X + 527, dialogStartY }, *pSTextSlidCels, 3); int sx = PANEL_X + 109; for (int i = 0; i < 35; i++) { - CelDrawTo(out, { sx, dialogStartY }, *pSTextSlidCels, 5); - CelDrawTo(out, { sx, dialogStartY + ErrorWindowHeight - 6 }, *pSTextSlidCels, 7); + CelDrawTo(out, { sx, dialogStartY }, *pSTextSlidCels, 4); + CelDrawTo(out, { sx, dialogStartY + ErrorWindowHeight - 6 }, *pSTextSlidCels, 6); sx += 12; } int drawnYborder = 12; while ((drawnYborder + 12) < ErrorWindowHeight) { - CelDrawTo(out, { PANEL_X + 101, dialogStartY + drawnYborder }, *pSTextSlidCels, 6); - CelDrawTo(out, { PANEL_X + 527, dialogStartY + drawnYborder }, *pSTextSlidCels, 8); + CelDrawTo(out, { PANEL_X + 101, dialogStartY + drawnYborder }, *pSTextSlidCels, 5); + CelDrawTo(out, { PANEL_X + 527, dialogStartY + drawnYborder }, *pSTextSlidCels, 7); drawnYborder += 12; } diff --git a/Source/gmenu.cpp b/Source/gmenu.cpp index d337e4a89..36d7733bb 100644 --- a/Source/gmenu.cpp +++ b/Source/gmenu.cpp @@ -106,12 +106,12 @@ void GmenuDrawMenuItem(const Surface &out, TMenuItem *pItem, int y) int w = GmenuGetLineWidth(pItem); if ((pItem->dwFlags & GMENU_SLIDER) != 0) { int x = 16 + w / 2; - CelDrawTo(out, { x + PANEL_LEFT, y + 40 }, *optbar_cel, 1); + CelDrawTo(out, { x + PANEL_LEFT, y + 40 }, *optbar_cel, 0); uint16_t step = pItem->dwFlags & 0xFFF; uint16_t steps = std::max((pItem->dwFlags & 0xFFF000) >> 12, 2); uint16_t pos = step * 256 / steps; GmenuClearBuffer(out, x + 2 + PANEL_LEFT, y + 38, pos + 13, 28); - CelDrawTo(out, { x + 2 + pos + PANEL_LEFT, y + 38 }, *option_cel, 1); + CelDrawTo(out, { x + 2 + pos + PANEL_LEFT, y + 38 }, *option_cel, 0); } int x = (gnScreenWidth - w) / 2; @@ -179,7 +179,7 @@ void FreeGMenu() void gmenu_init_menu() { - LogoAnim_frame = 1; + LogoAnim_frame = 0; sgpCurrentMenu = nullptr; sgpCurrItem = nullptr; gmenu_current_option = nullptr; @@ -230,9 +230,9 @@ void gmenu_draw(const Surface &out) if (gbIsHellfire) { const uint32_t ticks = SDL_GetTicks(); if ((int)(ticks - LogoAnim_tick) > 25) { - LogoAnim_frame++; - if (LogoAnim_frame > 16) - LogoAnim_frame = 1; + ++LogoAnim_frame; + if (LogoAnim_frame >= 16) + LogoAnim_frame = 0; LogoAnim_tick = ticks; } } diff --git a/Source/interfac.cpp b/Source/interfac.cpp index a168a4433..be3046a53 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -172,7 +172,7 @@ void DrawCutscene() { const Surface &out = GlobalBackBuffer(); DrawArt(out, { PANEL_X - (ArtCutsceneWidescreen.w() - PANEL_WIDTH) / 2, UI_OFFSET_Y }, &ArtCutsceneWidescreen); - CelDrawTo(out, { PANEL_X, 480 - 1 + UI_OFFSET_Y }, *sgpBackCel, 1); + CelDrawTo(out, { PANEL_X, 480 - 1 + UI_OFFSET_Y }, *sgpBackCel, 0); constexpr int ProgressHeight = 22; SDL_Rect rect = MakeSdlRect( diff --git a/Source/inv.cpp b/Source/inv.cpp index f0de2c406..c9191cf61 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -1171,7 +1171,7 @@ void InitInv() void DrawInv(const Surface &out) { - CelDrawTo(out, GetPanelPosition(UiPanels::Inventory, { 0, 351 }), *pInvCels, 1); + CelDrawTo(out, GetPanelPosition(UiPanels::Inventory, { 0, 351 }), *pInvCels, 0); Size slotSize[] = { { 2, 2 }, // head @@ -1201,9 +1201,9 @@ void DrawInv(const Surface &out) int screenY = slotPos[slot].y; InvDrawSlotBack(out, GetPanelPosition(UiPanels::Inventory, { screenX, screenY }), { slotSize[slot].width * InventorySlotSizeInPixels.width, slotSize[slot].height * InventorySlotSizeInPixels.height }); - int frame = myPlayer.InvBody[slot]._iCurs + CURSOR_FIRSTITEM; + const int cursId = myPlayer.InvBody[slot]._iCurs + CURSOR_FIRSTITEM; - auto frameSize = GetInvItemSize(frame); + auto frameSize = GetInvItemSize(cursId); // calc item offsets for weapons smaller than 2x3 slots if (slot == INVLOC_HAND_LEFT) { @@ -1214,8 +1214,8 @@ void DrawInv(const Surface &out) screenY += frameSize.height == (3 * InventorySlotSizeInPixels.height) ? 0 : -INV_SLOT_HALF_SIZE_PX; } - const auto &cel = GetInvItemSprite(frame); - const int celFrame = GetInvItemFrame(frame); + const auto &cel = GetInvItemSprite(cursId); + const int celFrame = GetInvItemFrame(cursId); const Point position = GetPanelPosition(UiPanels::Inventory, { screenX, screenY }); if (pcursinvitem == slot) { @@ -1252,10 +1252,10 @@ void DrawInv(const Surface &out) for (int j = 0; j < NUM_INV_GRID_ELEM; j++) { if (myPlayer.InvGrid[j] > 0) { // first slot of an item int ii = myPlayer.InvGrid[j] - 1; - int frame = myPlayer.InvList[ii]._iCurs + CURSOR_FIRSTITEM; + int cursId = myPlayer.InvList[ii]._iCurs + CURSOR_FIRSTITEM; - const auto &cel = GetInvItemSprite(frame); - const int celFrame = GetInvItemFrame(frame); + const auto &cel = GetInvItemSprite(cursId); + const int celFrame = GetInvItemFrame(cursId); const Point position = GetPanelPosition(UiPanels::Inventory, InvRect[j + SLOTXY_INV_FIRST]) + Displacement { 0, -1 }; if (pcursinvitem == ii + INVITEM_INV_FIRST) { CelBlitOutlineTo( @@ -1291,10 +1291,10 @@ void DrawInvBelt(const Surface &out) const Point position { InvRect[i + SLOTXY_BELT_FIRST].x + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].y + PANEL_Y - 1 }; InvDrawSlotBack(out, position, InventorySlotSizeInPixels); - int frame = myPlayer.SpdList[i]._iCurs + CURSOR_FIRSTITEM; + const int cursId = myPlayer.SpdList[i]._iCurs + CURSOR_FIRSTITEM; - const auto &cel = GetInvItemSprite(frame); - const int celFrame = GetInvItemFrame(frame); + const auto &cel = GetInvItemSprite(cursId); + const int celFrame = GetInvItemFrame(cursId); if (pcursinvitem == i + INVITEM_BELT_FIRST) { if (ControlMode == ControlTypes::KeyboardAndMouse || invflag) { diff --git a/Source/items.cpp b/Source/items.cpp index 8f38ca3bd..dcdec308c 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -443,7 +443,7 @@ void AddInitItems() item._iCreateInfo = curlv | CF_PREGEN; SetupItem(item); - item.AnimInfo.CurrentFrame = item.AnimInfo.NumberOfFrames; + item.AnimInfo.CurrentFrame = item.AnimInfo.NumberOfFrames - 1; item._iAnimFlag = false; item._iSelFlag = 1; DeltaAddItem(ii); @@ -1686,7 +1686,7 @@ void SpawnRock() SetupItem(item); item._iSelFlag = 2; item._iPostDraw = true; - item.AnimInfo.CurrentFrame = 11; + item.AnimInfo.CurrentFrame = 10; } void ItemDoppel() @@ -1815,7 +1815,7 @@ void PrintItemOil(char iDidx) void DrawUniqueInfoWindow(const Surface &out) { - CelDrawTo(out, GetPanelPosition(UiPanels::Inventory, { 24 - SPANEL_WIDTH, 327 }), *pSTextBoxCels, 1); + CelDrawTo(out, GetPanelPosition(UiPanels::Inventory, { 24 - SPANEL_WIDTH, 327 }), *pSTextBoxCels, 0); DrawHalfTransparentRectTo(out, GetRightPanel().position.x - SPANEL_WIDTH + 27, GetRightPanel().position.y + 28, 265, 297); } @@ -3358,7 +3358,7 @@ void SpawnQuestItem(int itemid, Point position, int randarea, int selflag) item._iPostDraw = true; if (selflag != 0) { item._iSelFlag = selflag; - item.AnimInfo.CurrentFrame = item.AnimInfo.NumberOfFrames; + item.AnimInfo.CurrentFrame = item.AnimInfo.NumberOfFrames - 1; item._iAnimFlag = false; } } @@ -3442,16 +3442,16 @@ void ProcessItems() continue; item.AnimInfo.ProcessAnimation(); if (item._iCurs == ICURS_MAGIC_ROCK) { - if (item._iSelFlag == 1 && item.AnimInfo.CurrentFrame == 11) - item.AnimInfo.CurrentFrame = 1; - if (item._iSelFlag == 2 && item.AnimInfo.CurrentFrame == 21) - item.AnimInfo.CurrentFrame = 11; + if (item._iSelFlag == 1 && item.AnimInfo.CurrentFrame == 10) + item.AnimInfo.CurrentFrame = 0; + if (item._iSelFlag == 2 && item.AnimInfo.CurrentFrame == 20) + item.AnimInfo.CurrentFrame = 10; } else { - if (item.AnimInfo.CurrentFrame == item.AnimInfo.NumberOfFrames / 2) + if (item.AnimInfo.CurrentFrame == (item.AnimInfo.NumberOfFrames - 1) / 2) PlaySfxLoc(ItemDropSnds[ItemCAnimTbl[item._iCurs]], item.position); - if (item.AnimInfo.CurrentFrame >= item.AnimInfo.NumberOfFrames) { - item.AnimInfo.CurrentFrame = item.AnimInfo.NumberOfFrames; + if (item.AnimInfo.CurrentFrame >= item.AnimInfo.NumberOfFrames - 1) { + item.AnimInfo.CurrentFrame = item.AnimInfo.NumberOfFrames - 1; item._iAnimFlag = false; item._iSelFlag = 1; } @@ -4360,7 +4360,7 @@ void MakeGoldStack(Item &goldItem, int value) int ItemNoFlippy() { int r = ActiveItems[ActiveItemCount - 1]; - Items[r].AnimInfo.CurrentFrame = Items[r].AnimInfo.NumberOfFrames; + Items[r].AnimInfo.CurrentFrame = Items[r].AnimInfo.NumberOfFrames - 1; Items[r]._iAnimFlag = false; Items[r]._iSelFlag = 1; @@ -4617,7 +4617,7 @@ void Item::SetNewAnimation(bool showAnimation) _iAnimFlag = true; _iSelFlag = 0; } else { - AnimInfo.CurrentFrame = AnimInfo.NumberOfFrames; + AnimInfo.CurrentFrame = AnimInfo.NumberOfFrames - 1; _iAnimFlag = false; _iSelFlag = 1; } diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index d3cae16d3..a9ec3188b 100644 --- a/Source/loadsave.cpp +++ b/Source/loadsave.cpp @@ -230,7 +230,7 @@ void LoadItemData(LoadHelper &file, Item &item) file.Skip(4); // Skip pointer _iAnimData item.AnimInfo = {}; item.AnimInfo.NumberOfFrames = file.NextLE(); - item.AnimInfo.CurrentFrame = file.NextLE(); + item.AnimInfo.CurrentFrame = file.NextLE() - 1; file.Skip(8); // Skip _iAnimWidth and _iAnimWidth2 file.Skip(4); // Unused since 1.02 item._iSelFlag = file.NextLE(); @@ -344,7 +344,7 @@ void LoadPlayer(LoadHelper &file, Player &player) player.AnimInfo.TicksPerFrame = file.NextLE() + 1; player.AnimInfo.TickCounterOfCurrentFrame = file.NextLE(); player.AnimInfo.NumberOfFrames = file.NextLE(); - player.AnimInfo.CurrentFrame = file.NextLE(); + player.AnimInfo.CurrentFrame = file.NextLE() - 1; file.Skip(4); // Skip _pAnimWidth file.Skip(4); // Skip _pAnimWidth2 file.Skip(4); // Skip _peflag @@ -581,7 +581,7 @@ void LoadMonster(LoadHelper *file, Monster &monster) monster.AnimInfo.TicksPerFrame = file->NextLE(); monster.AnimInfo.TickCounterOfCurrentFrame = file->NextLE(); monster.AnimInfo.NumberOfFrames = file->NextLE(); - monster.AnimInfo.CurrentFrame = file->NextLE(); + monster.AnimInfo.CurrentFrame = file->NextLE() - 1; file->Skip(4); // Skip _meflag monster._mDelFlag = file->NextBool32(); monster._mVar1 = file->NextLE(); @@ -953,7 +953,7 @@ void SaveItem(SaveHelper &file, const Item &item) file.WriteLE(item._iAnimFlag ? 1 : 0); file.Skip(4); // Skip pointer _iAnimData file.WriteLE(item.AnimInfo.NumberOfFrames); - file.WriteLE(item.AnimInfo.CurrentFrame); + file.WriteLE(item.AnimInfo.CurrentFrame + 1); // write _iAnimWidth for vanilla compatibility file.WriteLE(ItemAnimWidth); // write _iAnimWidth2 for vanilla compatibility @@ -1062,7 +1062,7 @@ void SavePlayer(SaveHelper &file, const Player &player) file.WriteLE(std::max(0, player.AnimInfo.TicksPerFrame - 1)); file.WriteLE(player.AnimInfo.TickCounterOfCurrentFrame); file.WriteLE(player.AnimInfo.NumberOfFrames); - file.WriteLE(player.AnimInfo.CurrentFrame); + file.WriteLE(player.AnimInfo.CurrentFrame + 1); // write _pAnimWidth for vanilla compatibility int animWidth = player.AnimInfo.celSprite ? player.AnimInfo.celSprite->Width() : 96; file.WriteLE(animWidth); @@ -1290,7 +1290,7 @@ void SaveMonster(SaveHelper *file, Monster &monster) file->WriteLE(monster.AnimInfo.TicksPerFrame); file->WriteLE(monster.AnimInfo.TickCounterOfCurrentFrame); file->WriteLE(monster.AnimInfo.NumberOfFrames); - file->WriteLE(monster.AnimInfo.CurrentFrame); + file->WriteLE(monster.AnimInfo.CurrentFrame + 1); file->Skip(); // Skip _meflag file->WriteLE(monster._mDelFlag ? 1 : 0); file->WriteLE(monster._mVar1); diff --git a/Source/minitext.cpp b/Source/minitext.cpp index ae89a1045..d5ee85f6a 100644 --- a/Source/minitext.cpp +++ b/Source/minitext.cpp @@ -145,7 +145,7 @@ void InitQTextMsg(_speech_id m) void DrawQTextBack(const Surface &out) { - CelDrawTo(out, { PANEL_X + 24, 327 + UI_OFFSET_Y }, *pTextBoxCels, 1); + CelDrawTo(out, { PANEL_X + 24, 327 + UI_OFFSET_Y }, *pTextBoxCels, 0); DrawHalfTransparentRectTo(out, PANEL_X + 27, UI_OFFSET_Y + 28, 585, 297); } diff --git a/Source/missiles.h b/Source/missiles.h index 26d6f0568..6977c0eee 100644 --- a/Source/missiles.h +++ b/Source/missiles.h @@ -101,7 +101,7 @@ struct Missile { int16_t _miAnimWidth2; int _miAnimCnt; // Increases by one each game tick, counting how close we are to _pAnimDelay int _miAnimAdd; - int _miAnimFrame; // Current frame of animation. + int _miAnimFrame; // Current frame of animation + 1. bool _miDrawFlag; bool _miLightFlag; bool _miPreFlag; diff --git a/Source/monster.cpp b/Source/monster.cpp index 8ec8d638e..c6a82efd3 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -157,12 +157,14 @@ void InitMonsterTRN(CMonster &monst) AnimStruct &anim = monst.Anims[i]; if (IsDirectionalAnim(monst, i)) { - for (int j = 0; j < 8; j++) { + for (size_t j = 0; j < 8; ++j) { Cl2ApplyTrans(anim.CelSpritesForDirections[j], colorTranslations, anim.Frames); } } else { - for (int j = 0; j < 8; j++) { - Cl2ApplyTrans(CelGetFrame(anim.CelSpritesForDirections[0], j), colorTranslations, anim.Frames); + byte *frames[8]; + CelGetDirectionFrames(anim.CelSpritesForDirections[0], frames); + for (byte *frame : frames) { + Cl2ApplyTrans(frame, colorTranslations, anim.Frames); } } } @@ -182,7 +184,7 @@ void InitMonster(Monster &monster, Direction rd, int mtype, Point position) monster.AnimInfo = {}; monster.ChangeAnimationData(MonsterGraphic::Stand); monster.AnimInfo.TickCounterOfCurrentFrame = GenerateRnd(monster.AnimInfo.TicksPerFrame - 1); - monster.AnimInfo.CurrentFrame = GenerateRnd(monster.AnimInfo.NumberOfFrames - 1) + 1; + monster.AnimInfo.CurrentFrame = GenerateRnd(monster.AnimInfo.NumberOfFrames - 1); monster.mLevel = monster.MData->mLevel; int maxhp = monster.MData->mMinHP + GenerateRnd(monster.MData->mMaxHP - monster.MData->mMinHP + 1); @@ -226,7 +228,7 @@ void InitMonster(Monster &monster, Direction rd, int mtype, Point position) if (monster._mAi == AI_GARG) { monster.ChangeAnimationData(MonsterGraphic::Special); - monster.AnimInfo.CurrentFrame = 1; + monster.AnimInfo.CurrentFrame = 0; monster._mFlags |= MFLAG_ALLOW_SPECIAL; monster._mmode = MonsterMode::SpecialMeleeAttack; } @@ -362,7 +364,7 @@ void PlaceGroup(int mtype, int num, UniqueMonsterPack uniqueMonsterPack, int lea if (minion._mAi != AI_GARG) { minion.ChangeAnimationData(MonsterGraphic::Stand); - minion.AnimInfo.CurrentFrame = GenerateRnd(minion.AnimInfo.NumberOfFrames - 1) + 1; + minion.AnimInfo.CurrentFrame = GenerateRnd(minion.AnimInfo.NumberOfFrames - 1); minion._mFlags &= ~MFLAG_ALLOW_SPECIAL; minion._mmode = MonsterMode::Stand; } @@ -1186,7 +1188,7 @@ void StartFadein(Monster &monster, Direction md, bool backwards) monster._mFlags &= ~MFLAG_HIDDEN; if (backwards) { monster._mFlags |= MFLAG_LOCK_ANIMATION; - monster.AnimInfo.CurrentFrame = monster.AnimInfo.NumberOfFrames; + monster.AnimInfo.CurrentFrame = monster.AnimInfo.NumberOfFrames - 1; } } @@ -1199,14 +1201,14 @@ void StartFadeout(Monster &monster, Direction md, bool backwards) monster.position.old = monster.position.tile; if (backwards) { monster._mFlags |= MFLAG_LOCK_ANIMATION; - monster.AnimInfo.CurrentFrame = monster.AnimInfo.NumberOfFrames; + monster.AnimInfo.CurrentFrame = monster.AnimInfo.NumberOfFrames - 1; } } void StartHeal(Monster &monster) { monster.ChangeAnimationData(MonsterGraphic::Special); - monster.AnimInfo.CurrentFrame = monster.MType->GetAnimData(MonsterGraphic::Special).Frames; + monster.AnimInfo.CurrentFrame = monster.MType->GetAnimData(MonsterGraphic::Special).Frames - 1; monster._mFlags |= MFLAG_LOCK_ANIMATION; monster._mmode = MonsterMode::Heal; monster._mVar1 = monster._mmaxhp / (16 * (GenerateRnd(5) + 4)); @@ -1228,7 +1230,7 @@ bool MonsterIdle(Monster &monster) else monster.ChangeAnimationData(MonsterGraphic::Stand); - if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames) + if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1) UpdateEnemy(monster); monster._mVar2++; @@ -1246,7 +1248,7 @@ bool MonsterWalk(int i, MonsterMode variant) assert(monster.MType != nullptr); // Check if we reached new tile - bool isAnimationEnd = monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames; + const bool isAnimationEnd = monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1; if (isAnimationEnd) { switch (variant) { case MonsterMode::MoveNorthwards: @@ -1454,22 +1456,22 @@ bool MonsterAttack(int i) assert(monster.MType != nullptr); assert(monster.MData != nullptr); - if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum) { + if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum - 1) { MonsterAttackPlayer(i, monster._menemy, monster.mHit, monster.mMinDamage, monster.mMaxDamage); if (monster._mAi != AI_SNAKE) PlayEffect(monster, 0); } - if (monster.MType->mtype >= MT_NMAGMA && monster.MType->mtype <= MT_WMAGMA && monster.AnimInfo.CurrentFrame == 9) { + if (monster.MType->mtype >= MT_NMAGMA && monster.MType->mtype <= MT_WMAGMA && monster.AnimInfo.CurrentFrame == 8) { MonsterAttackPlayer(i, monster._menemy, monster.mHit + 10, monster.mMinDamage - 2, monster.mMaxDamage - 2); PlayEffect(monster, 0); } - if (monster.MType->mtype >= MT_STORM && monster.MType->mtype <= MT_MAEL && monster.AnimInfo.CurrentFrame == 13) { + if (monster.MType->mtype >= MT_STORM && monster.MType->mtype <= MT_MAEL && monster.AnimInfo.CurrentFrame == 12) { MonsterAttackPlayer(i, monster._menemy, monster.mHit - 20, monster.mMinDamage + 4, monster.mMaxDamage + 4); PlayEffect(monster, 0); } - if (monster._mAi == AI_SNAKE && monster.AnimInfo.CurrentFrame == 1) + if (monster._mAi == AI_SNAKE && monster.AnimInfo.CurrentFrame == 0) PlayEffect(monster, 0); - if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames) { + if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1) { M_StartStand(monster, monster._mdir); return true; } @@ -1484,7 +1486,7 @@ bool MonaterRangedAttack(int i) assert(monster.MType != nullptr); assert(monster.MData != nullptr); - if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum) { + if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum - 1) { const auto &missileType = static_cast(monster._mVar1); if (missileType != MIS_NULL) { int multimissiles = 1; @@ -1505,7 +1507,7 @@ bool MonaterRangedAttack(int i) PlayEffect(monster, 0); } - if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames) { + if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1) { M_StartStand(monster, monster._mdir); return true; } @@ -1520,7 +1522,7 @@ bool MonsterRangedSpecialAttack(int i) assert(monster.MType != nullptr); assert(monster.MData != nullptr); - if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum2 && monster.AnimInfo.TickCounterOfCurrentFrame == 0) { + if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum2 - 1 && monster.AnimInfo.TickCounterOfCurrentFrame == 0) { if (AddMissile( monster.position.tile, monster.enemyPosition, @@ -1535,7 +1537,7 @@ bool MonsterRangedSpecialAttack(int i) } } - if (monster._mAi == AI_MEGA && monster.AnimInfo.CurrentFrame == monster.MData->mAFNum2) { + if (monster._mAi == AI_MEGA && monster.AnimInfo.CurrentFrame == monster.MData->mAFNum2 - 1) { if (monster._mVar2++ == 0) { monster._mFlags |= MFLAG_ALLOW_SPECIAL; } else if (monster._mVar2 == 15) { @@ -1543,7 +1545,7 @@ bool MonsterRangedSpecialAttack(int i) } } - if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames) { + if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1) { M_StartStand(monster, monster._mdir); return true; } @@ -1558,10 +1560,10 @@ bool MonsterSpecialAttack(int i) assert(monster.MType != nullptr); assert(monster.MData != nullptr); - if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum2) + if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum2 - 1) MonsterAttackPlayer(i, monster._menemy, monster.mHit2, monster.mMinDamage2, monster.mMaxDamage2); - if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames) { + if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1) { M_StartStand(monster, monster._mdir); return true; } @@ -1571,8 +1573,8 @@ bool MonsterSpecialAttack(int i) bool MonsterFadein(Monster &monster) { - if (((monster._mFlags & MFLAG_LOCK_ANIMATION) == 0 || monster.AnimInfo.CurrentFrame != 1) - && ((monster._mFlags & MFLAG_LOCK_ANIMATION) != 0 || monster.AnimInfo.CurrentFrame != monster.AnimInfo.NumberOfFrames)) { + if (((monster._mFlags & MFLAG_LOCK_ANIMATION) == 0 || monster.AnimInfo.CurrentFrame != 0) + && ((monster._mFlags & MFLAG_LOCK_ANIMATION) != 0 || monster.AnimInfo.CurrentFrame != monster.AnimInfo.NumberOfFrames - 1)) { return false; } @@ -1584,8 +1586,8 @@ bool MonsterFadein(Monster &monster) bool MonsterFadeout(Monster &monster) { - if (((monster._mFlags & MFLAG_LOCK_ANIMATION) == 0 || monster.AnimInfo.CurrentFrame != 1) - && ((monster._mFlags & MFLAG_LOCK_ANIMATION) != 0 || monster.AnimInfo.CurrentFrame != monster.AnimInfo.NumberOfFrames)) { + if (((monster._mFlags & MFLAG_LOCK_ANIMATION) == 0 || monster.AnimInfo.CurrentFrame != 0) + && ((monster._mFlags & MFLAG_LOCK_ANIMATION) != 0 || monster.AnimInfo.CurrentFrame != monster.AnimInfo.NumberOfFrames - 1)) { return false; } @@ -1610,7 +1612,7 @@ bool MonsterHeal(Monster &monster) return false; } - if (monster.AnimInfo.CurrentFrame == 1) { + if (monster.AnimInfo.CurrentFrame == 0) { monster._mFlags &= ~MFLAG_LOCK_ANIMATION; monster._mFlags |= MFLAG_ALLOW_SPECIAL; if (monster._mVar1 + monster._mhitpoints < monster._mmaxhp) { @@ -1688,7 +1690,7 @@ bool MonsterTalk(Monster &monster) bool MonsterGotHit(Monster &monster) { - if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames) { + if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1) { M_StartStand(monster, monster._mdir); return true; @@ -1719,7 +1721,7 @@ bool MonsterDeath(int i) if (monster._mVar1 == 140) PrepDoEnding(); - } else if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames) { + } else if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1) { if (monster._uniqtype == 0) AddCorpse(monster.position.tile, monster.MType->mdeadval, monster._mdir); else @@ -1735,10 +1737,10 @@ bool MonsterDeath(int i) bool MonsterSpecialStand(Monster &monster) { - if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum2) + if (monster.AnimInfo.CurrentFrame == monster.MData->mAFNum2 - 1) PlayEffect(monster, 3); - if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames) { + if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1) { M_StartStand(monster, monster._mdir); return true; } @@ -2522,7 +2524,7 @@ void FallenAi(int i) } } - if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames) { + if (monster.AnimInfo.CurrentFrame == monster.AnimInfo.NumberOfFrames - 1) { if (GenerateRnd(4) != 0) { return; } @@ -3578,7 +3580,7 @@ void PrepareUniqueMonst(Monster &monster, int uniqindex, int miniontype, int bos if (monster._mAi != AI_GARG) { monster.ChangeAnimationData(MonsterGraphic::Stand); - monster.AnimInfo.CurrentFrame = GenerateRnd(monster.AnimInfo.NumberOfFrames - 1) + 1; + monster.AnimInfo.CurrentFrame = GenerateRnd(monster.AnimInfo.NumberOfFrames - 1); monster._mFlags &= ~MFLAG_ALLOW_SPECIAL; monster._mmode = MonsterMode::Stand; } @@ -3747,11 +3749,9 @@ void InitMonsterGFX(int monst) byte *cl2Data = &monster.animData[animOffsets[animIndex]]; if (IsDirectionalAnim(monster, animIndex)) { - for (int i = 0; i < 8; i++) { - anim.CelSpritesForDirections[i] = CelGetFrame(cl2Data, i); - } + CelGetDirectionFrames(cl2Data, anim.CelSpritesForDirections.data()); } else { - for (int i = 0; i < 8; i++) { + for (size_t i = 0; i < 8; ++i) { anim.CelSpritesForDirections[i] = cl2Data; } } @@ -4587,10 +4587,10 @@ void SyncMonsterAnim(Monster &monster) break; case MonsterMode::Charge: graphic = MonsterGraphic::Attack; - monster.AnimInfo.CurrentFrame = 1; + monster.AnimInfo.CurrentFrame = 0; break; default: - monster.AnimInfo.CurrentFrame = 1; + monster.AnimInfo.CurrentFrame = 0; break; } diff --git a/Source/msg.cpp b/Source/msg.cpp index e783c6468..87c0f9b13 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -1696,7 +1696,7 @@ DWORD OnPlayerJoinLevel(const TCmd *pCmd, int pnum) player._pgfxnum &= ~0xF; player._pmode = PM_DEATH; NewPlrAnim(player, player_graphic::Death, Direction::South, player._pDFrames, 1); - player.AnimInfo.CurrentFrame = player.AnimInfo.NumberOfFrames - 1; + player.AnimInfo.CurrentFrame = player.AnimInfo.NumberOfFrames - 2; dFlags[player.position.tile.x][player.position.tile.y] |= DungeonFlag::DeadPlayer; } diff --git a/Source/multi.cpp b/Source/multi.cpp index f5c5aa3b6..2defc05f7 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -804,7 +804,7 @@ void recv_plrinfo(int pnum, const TCmdPlrInfoHdr &header, bool recv) player._pgfxnum &= ~0xF; player._pmode = PM_DEATH; NewPlrAnim(player, player_graphic::Death, Direction::South, player._pDFrames, 1); - player.AnimInfo.CurrentFrame = player.AnimInfo.NumberOfFrames - 1; + player.AnimInfo.CurrentFrame = player.AnimInfo.NumberOfFrames - 2; dFlags[player.position.tile.x][player.position.tile.y] |= DungeonFlag::DeadPlayer; } diff --git a/Source/panels/charpanel.cpp b/Source/panels/charpanel.cpp index 11512268b..5213d755f 100644 --- a/Source/panels/charpanel.cpp +++ b/Source/panels/charpanel.cpp @@ -248,13 +248,13 @@ void DrawStatButtons(const Surface &out) { if (MyPlayer->_pStatPts > 0) { if (MyPlayer->_pBaseStr < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Strength)) - CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 157 }), *pChrButtons, chrbtn[static_cast(CharacterAttribute::Strength)] ? 3 : 2); + CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 157 }), *pChrButtons, chrbtn[static_cast(CharacterAttribute::Strength)] ? 2 : 1); if (MyPlayer->_pBaseMag < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Magic)) - CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 185 }), *pChrButtons, chrbtn[static_cast(CharacterAttribute::Magic)] ? 5 : 4); + CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 185 }), *pChrButtons, chrbtn[static_cast(CharacterAttribute::Magic)] ? 4 : 3); if (MyPlayer->_pBaseDex < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Dexterity)) - CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 214 }), *pChrButtons, chrbtn[static_cast(CharacterAttribute::Dexterity)] ? 7 : 6); + CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 214 }), *pChrButtons, chrbtn[static_cast(CharacterAttribute::Dexterity)] ? 6 : 5); if (MyPlayer->_pBaseVit < MyPlayer->GetMaximumAttributeValue(CharacterAttribute::Vitality)) - CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 242 }), *pChrButtons, chrbtn[static_cast(CharacterAttribute::Vitality)] ? 9 : 8); + CelDrawTo(out, GetPanelPosition(UiPanels::Character, { 137, 242 }), *pChrButtons, chrbtn[static_cast(CharacterAttribute::Vitality)] ? 8 : 7); } } diff --git a/Source/panels/spell_book.cpp b/Source/panels/spell_book.cpp index 6f359aa2a..b0c33e1c9 100644 --- a/Source/panels/spell_book.cpp +++ b/Source/panels/spell_book.cpp @@ -85,7 +85,7 @@ void InitSpellBook() pSpellBkCel = LoadCel("Data\\SpellBk.CEL", SPANEL_WIDTH); if (gbIsHellfire) { - static const uint16_t SBkBtnHellfireWidths[] = { 0, 61, 61, 61, 61, 61, 76 }; + static const uint16_t SBkBtnHellfireWidths[] = { 61, 61, 61, 61, 61, 76 }; pSBkBtnCel = LoadCel("Data\\SpellBkB.CEL", SBkBtnHellfireWidths); } else { pSBkBtnCel = LoadCel("Data\\SpellBkB.CEL", 76); @@ -117,16 +117,16 @@ void FreeSpellBook() void DrawSpellBook(const Surface &out) { - CelDrawTo(out, GetPanelPosition(UiPanels::Spell, { 0, 351 }), *pSpellBkCel, 1); + CelDrawTo(out, GetPanelPosition(UiPanels::Spell, { 0, 351 }), *pSpellBkCel, 0); if (gbIsHellfire && sbooktab < 5) { - CelDrawTo(out, GetPanelPosition(UiPanels::Spell, { 61 * sbooktab + 7, 348 }), *pSBkBtnCel, sbooktab + 1); + CelDrawTo(out, GetPanelPosition(UiPanels::Spell, { 61 * sbooktab + 7, 348 }), *pSBkBtnCel, sbooktab); } else { // BUGFIX: rendering of page 3 and page 4 buttons are both off-by-one pixel (fixed). int sx = 76 * sbooktab + 7; if (sbooktab == 2 || sbooktab == 3) { sx++; } - CelDrawTo(out, GetPanelPosition(UiPanels::Spell, { sx, 348 }), *pSBkBtnCel, sbooktab + 1); + CelDrawTo(out, GetPanelPosition(UiPanels::Spell, { sx, 348 }), *pSBkBtnCel, sbooktab); } auto &player = Players[MyPlayerId]; uint64_t spl = player._pMemSpells | player._pISpells | player._pAblSpells; diff --git a/Source/panels/spell_icons.cpp b/Source/panels/spell_icons.cpp index ad2db2a48..9da446e08 100644 --- a/Source/panels/spell_icons.cpp +++ b/Source/panels/spell_icons.cpp @@ -14,7 +14,8 @@ uint8_t SplTransTbl[256]; } // namespace const char SpellITbl[] = { - 27, + 26, + 0, 1, 2, 3, @@ -23,49 +24,48 @@ const char SpellITbl[] = { 6, 7, 8, - 9, - 28, - 13, + 27, 12, + 11, + 17, + 15, + 13, + 17, 18, - 16, - 14, - 18, + 10, 19, - 11, + 14, 20, - 15, - 21, + 22, 23, 24, + 21, 25, - 22, - 26, - 29, + 28, + 36, 37, 38, - 39, - 42, 41, 40, - 10, - 36, - 30, - 51, - 51, + 39, + 9, + 35, + 29, + 50, 50, + 49, + 45, 46, + 42, + 44, 47, - 43, - 45, 48, - 49, - 44, - 35, - 35, - 35, - 35, - 35, + 43, + 34, + 34, + 34, + 34, + 34, }; void LoadSpellIcons() diff --git a/Source/panels/spell_list.cpp b/Source/panels/spell_list.cpp index f5074d618..03da2acfa 100644 --- a/Source/panels/spell_list.cpp +++ b/Source/panels/spell_list.cpp @@ -113,7 +113,7 @@ void DrawSpell(const Surface &out) if (currlevel == 0 && st != RSPLTYPE_INVALID && !spelldata[spl].sTownSpell) st = RSPLTYPE_INVALID; SetSpellTrans(st); - const int nCel = (spl != SPL_INVALID) ? SpellITbl[spl] : 27; + const int nCel = (spl != SPL_INVALID) ? SpellITbl[spl] : 26; const Point position { PANEL_X + 565, PANEL_Y + 119 }; DrawSpellCel(out, position, nCel); diff --git a/Source/player.cpp b/Source/player.cpp index ab9857afa..98b9b7856 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -371,9 +371,10 @@ void SetPlayerGPtrs(const char *path, std::unique_ptr &data, std::array< if (data == nullptr && gbQuietMode) return; - for (int i = 0; i < 8; i++) { - byte *pCelStart = CelGetFrame(data.get(), i); - anim[i].emplace(pCelStart, width); + const byte *directionFrames[8]; + CelGetDirectionFrames(data.get(), directionFrames); + for (size_t i = 0; i < 8; i++) { + anim[i].emplace(directionFrames[i], width); } } @@ -667,14 +668,14 @@ bool DoWalk(int pnum, int variant) // Play walking sound effect on certain animation frames if (*sgOptions.Audio.walkingSound && (currlevel != 0 || sgGameInitInfo.bRunInTown == 0)) { - if (player.AnimInfo.CurrentFrame == 1 - || player.AnimInfo.CurrentFrame == 5) { + if (player.AnimInfo.CurrentFrame == 0 + || player.AnimInfo.CurrentFrame == 4) { PlaySfxLoc(PS_WALK1, player.position.tile); } } // Check if we reached new tile - if (player.AnimInfo.CurrentFrame >= player._pWFrames) { + if (player.AnimInfo.CurrentFrame >= player._pWFrames - 1) { // Update the player's tile position switch (variant) { @@ -1113,13 +1114,13 @@ bool DoAttack(int pnum) } auto &player = Players[pnum]; - if (player.AnimInfo.CurrentFrame == player._pAFNum - 1) { + if (player.AnimInfo.CurrentFrame == player._pAFNum - 2) { PlaySfxLoc(PS_SWING, player.position.tile); } bool didhit = false; - if (player.AnimInfo.CurrentFrame == player._pAFNum) { + if (player.AnimInfo.CurrentFrame == player._pAFNum - 1) { Point position = player.position.tile + player._pdir; int dx = position.x; int dy = position.y; @@ -1206,7 +1207,7 @@ bool DoAttack(int pnum) } } - if (player.AnimInfo.CurrentFrame == player._pAFrames) { + if (player.AnimInfo.CurrentFrame == player._pAFrames - 1) { StartStand(pnum, player._pdir); ClearStateVariables(player); return true; @@ -1223,10 +1224,10 @@ bool DoRangeAttack(int pnum) auto &player = Players[pnum]; int arrows = 0; - if (player.AnimInfo.CurrentFrame == player._pAFNum) { + if (player.AnimInfo.CurrentFrame == player._pAFNum - 1) { arrows = 1; } - if ((player._pIFlags & ISPL_MULT_ARROWS) != 0 && player.AnimInfo.CurrentFrame == player._pAFNum + 2) { + if ((player._pIFlags & ISPL_MULT_ARROWS) != 0 && player.AnimInfo.CurrentFrame == player._pAFNum + 1) { arrows = 2; } @@ -1277,7 +1278,7 @@ bool DoRangeAttack(int pnum) } } - if (player.AnimInfo.CurrentFrame >= player._pAFrames) { + if (player.AnimInfo.CurrentFrame >= player._pAFrames - 1) { StartStand(pnum, player._pdir); ClearStateVariables(player); return true; @@ -1326,7 +1327,7 @@ bool DoBlock(int pnum) } auto &player = Players[pnum]; - if (player.AnimInfo.CurrentFrame >= player._pBFrames) { + if (player.AnimInfo.CurrentFrame >= player._pBFrames - 1) { StartStand(pnum, player._pdir); ClearStateVariables(player); @@ -1395,7 +1396,7 @@ bool DoSpell(int pnum) auto &player = Players[pnum]; int currentSpellFrame = leveltype != DTYPE_TOWN ? player.AnimInfo.CurrentFrame : ((player.AnimInfo.CurrentFrame * player.AnimInfo.TicksPerFrame) + player.AnimInfo.TickCounterOfCurrentFrame); - if (currentSpellFrame == (player._pSFNum + 1)) { + if (currentSpellFrame == player._pSFNum) { CastSpell( pnum, player._pSpell, @@ -1410,7 +1411,7 @@ bool DoSpell(int pnum) } } - if (currentSpellFrame >= player._pSFrames) { + if (currentSpellFrame >= player._pSFrames - 1) { StartStand(pnum, player._pdir); ClearStateVariables(player); return true; @@ -1426,7 +1427,7 @@ bool DoGotHit(int pnum) } auto &player = Players[pnum]; - if (player.AnimInfo.CurrentFrame >= player._pHFrames) { + if (player.AnimInfo.CurrentFrame >= player._pHFrames - 1) { StartStand(pnum, player._pdir); ClearStateVariables(player); if (GenerateRnd(4) != 0) { @@ -1446,7 +1447,7 @@ bool DoDeath(int pnum) } auto &player = Players[pnum]; - if (player.AnimInfo.CurrentFrame == player.AnimInfo.NumberOfFrames) { + if (player.AnimInfo.CurrentFrame == player.AnimInfo.NumberOfFrames - 1) { if (player.AnimInfo.TickCounterOfCurrentFrame == 0) { player.AnimInfo.TicksPerFrame = 1000000000; dFlags[player.position.tile.x][player.position.tile.y] |= DungeonFlag::DeadPlayer; @@ -1728,7 +1729,7 @@ void CheckNewPath(int pnum, bool pmWillBeCalled) return; } - if (player._pmode == PM_ATTACK && player.AnimInfo.CurrentFrame > player._pAFNum) { + if (player._pmode == PM_ATTACK && player.AnimInfo.CurrentFrame >= player._pAFNum) { if (player.destAction == ACTION_ATTACK) { d = GetDirection(player.position.future, { player.destParam1, player.destParam2 }); StartAttack(pnum, d); @@ -1759,7 +1760,7 @@ void CheckNewPath(int pnum, bool pmWillBeCalled) } } - if (player._pmode == PM_RATTACK && player.AnimInfo.CurrentFrame > player._pAFNum) { + if (player._pmode == PM_RATTACK && player.AnimInfo.CurrentFrame >= player._pAFNum) { if (player.destAction == ACTION_RATTACK) { d = GetDirection(player.position.tile, { player.destParam1, player.destParam2 }); StartRangeAttack(pnum, d, player.destParam1, player.destParam2); @@ -1775,8 +1776,8 @@ void CheckNewPath(int pnum, bool pmWillBeCalled) } } - int currentSpellFrame = leveltype != DTYPE_TOWN ? player.AnimInfo.CurrentFrame : (player.AnimInfo.CurrentFrame * (player.AnimInfo.TicksPerFrame + 1) + player.AnimInfo.TickCounterOfCurrentFrame); - if (player._pmode == PM_SPELL && currentSpellFrame > player._pSFNum) { + const int currentSpellFrame = leveltype != DTYPE_TOWN ? player.AnimInfo.CurrentFrame : (player.AnimInfo.CurrentFrame * (player.AnimInfo.TicksPerFrame + 1) + player.AnimInfo.TickCounterOfCurrentFrame); + if (player._pmode == PM_SPELL && currentSpellFrame >= player._pSFNum) { if (player.destAction == ACTION_SPELL) { d = GetDirection(player.position.tile, { player.destParam1, player.destParam2 }); StartSpell(pnum, d, player.destParam1, player.destParam2); @@ -2832,12 +2833,12 @@ void InitPlayer(Player &player, bool firstTime) if (player._pHitPoints >> 6 > 0) { player._pmode = PM_STAND; NewPlrAnim(player, player_graphic::Stand, Direction::South, player._pNFrames, 4); - player.AnimInfo.CurrentFrame = GenerateRnd(player._pNFrames - 1) + 1; + player.AnimInfo.CurrentFrame = GenerateRnd(player._pNFrames - 1); player.AnimInfo.TickCounterOfCurrentFrame = GenerateRnd(3); } else { player._pmode = PM_DEATH; NewPlrAnim(player, player_graphic::Death, Direction::South, player._pDFrames, 2); - player.AnimInfo.CurrentFrame = player.AnimInfo.NumberOfFrames - 1; + player.AnimInfo.CurrentFrame = player.AnimInfo.NumberOfFrames - 2; } player._pdir = Direction::South; diff --git a/Source/player.h b/Source/player.h index 65809916a..86f62321e 100644 --- a/Source/player.h +++ b/Source/player.h @@ -693,13 +693,13 @@ struct Player { { if (_pmode == PM_STAND) return true; - if (_pmode == PM_ATTACK && AnimInfo.CurrentFrame > _pAFNum) + if (_pmode == PM_ATTACK && AnimInfo.CurrentFrame >= _pAFNum) return true; - if (_pmode == PM_RATTACK && AnimInfo.CurrentFrame > _pAFNum) + if (_pmode == PM_RATTACK && AnimInfo.CurrentFrame >= _pAFNum) return true; - if (_pmode == PM_SPELL && AnimInfo.CurrentFrame > _pSFNum) + if (_pmode == PM_SPELL && AnimInfo.CurrentFrame >= _pSFNum) return true; - if (IsWalking() && AnimInfo.CurrentFrame == AnimInfo.NumberOfFrames) + if (IsWalking() && AnimInfo.CurrentFrame == AnimInfo.NumberOfFrames - 1) return true; return false; } diff --git a/Source/qol/stash.cpp b/Source/qol/stash.cpp index 2c65f24a9..388579211 100644 --- a/Source/qol/stash.cpp +++ b/Source/qol/stash.cpp @@ -613,7 +613,7 @@ void DrawGoldWithdraw(const Surface &out, int amount) const int dialogX = 30; - CelDrawTo(out, GetPanelPosition(UiPanels::Stash, { dialogX, 178 }), *pGBoxBuff, 1); + CelDrawTo(out, GetPanelPosition(UiPanels::Stash, { dialogX, 178 }), *pGBoxBuff, 0); // Pre-wrap the string at spaces, otherwise DrawString would hard wrap in the middle of words const std::string wrapped = WordWrapString(_("How many gold pieces do you want to withdraw?"), 200); diff --git a/Source/quests.cpp b/Source/quests.cpp index dc30c3481..5ae789325 100644 --- a/Source/quests.cpp +++ b/Source/quests.cpp @@ -743,7 +743,7 @@ void DrawQuestLog(const Surface &out) SelectedQuest = l; } const auto x = InnerPanel.position.x; - CelDrawTo(out, GetPanelPosition(UiPanels::Quest, { 0, 351 }), *pQLogCel, 1); + CelDrawTo(out, GetPanelPosition(UiPanels::Quest, { 0, 351 }), *pQLogCel, 0); int y = InnerPanel.position.y + ListYOffset; for (int i = 0; i < EncounteredQuestCount; i++) { if (i == FirstFinishedQuest) { diff --git a/Source/scrollrt.cpp b/Source/scrollrt.cpp index be152b625..8aa963bb0 100644 --- a/Source/scrollrt.cpp +++ b/Source/scrollrt.cpp @@ -312,9 +312,9 @@ void DrawMissilePrivate(const Surface &out, const Missile &missile, Point target Log("Draw Missile 2 type {}: NULL Cel Buffer", missile._mitype); return; } - int nCel = missile._miAnimFrame; + int nCel = missile._miAnimFrame - 1; const uint32_t frames = LoadLE32(missile._miAnimData); - if (nCel < 1 || frames > 50 || nCel > static_cast(frames)) { + if (nCel < 0 || frames > 50 || nCel >= static_cast(frames)) { Log("Draw Missile 2: frame {} of {}, missile type=={}", nCel, frames, missile._mitype); return; } @@ -322,11 +322,11 @@ void DrawMissilePrivate(const Surface &out, const Missile &missile, Point target const Point missileRenderPosition { targetBufferPosition + missile.position.offsetForRendering - Displacement { missile._miAnimWidth2, 0 } }; CelSprite cel { missile._miAnimData, missile._miAnimWidth }; if (missile._miUniqTrans != 0) - Cl2DrawTRN(out, missileRenderPosition.x, missileRenderPosition.y, cel, missile._miAnimFrame, Monsters[missile._misource].uniqueTRN.get()); + Cl2DrawTRN(out, missileRenderPosition.x, missileRenderPosition.y, cel, nCel, Monsters[missile._misource].uniqueTRN.get()); else if (missile._miLightFlag) - Cl2DrawLight(out, missileRenderPosition.x, missileRenderPosition.y, cel, missile._miAnimFrame); + Cl2DrawLight(out, missileRenderPosition.x, missileRenderPosition.y, cel, nCel); else - Cl2Draw(out, missileRenderPosition.x, missileRenderPosition.y, cel, missile._miAnimFrame); + Cl2Draw(out, missileRenderPosition.x, missileRenderPosition.y, cel, nCel); } /** @@ -421,7 +421,7 @@ void DrawMonster(const Surface &out, Point tilePosition, Point targetBufferPosit int nCel = monster.AnimInfo.GetFrameToUseForRendering(); const uint32_t frames = LoadLE32(monster.AnimInfo.celSprite->Data()); - if (nCel < 1 || frames > 50 || nCel > static_cast(frames)) { + if (nCel < 0 || frames > 50 || nCel >= static_cast(frames)) { Log( "Draw Monster \"{}\" {}: facing {}, frame {} of {}", monster.mName, @@ -461,16 +461,16 @@ void DrawPlayerIconHelper(const Surface &out, int pnum, missile_graphic_id missi const CelSprite cel = MissileSpriteData[missileGraphicId].Sprite(); if (pnum == MyPlayerId) { - Cl2Draw(out, position.x, position.y, cel, 1); + Cl2Draw(out, position.x, position.y, cel, 0); return; } if (lighting) { - Cl2DrawTRN(out, position.x, position.y, cel, 1, GetInfravisionTRN()); + Cl2DrawTRN(out, position.x, position.y, cel, 0, GetInfravisionTRN()); return; } - Cl2DrawLight(out, position.x, position.y, cel, 1); + Cl2DrawLight(out, position.x, position.y, cel, 0); } /** @@ -512,7 +512,7 @@ void DrawPlayer(const Surface &out, int pnum, Point tilePosition, Point targetBu if (player.previewCelSprite) { sprite = player.previewCelSprite; - nCel = 1; + nCel = 0; } if (!sprite) { @@ -522,8 +522,8 @@ void DrawPlayer(const Surface &out, int pnum, Point tilePosition, Point targetBu Point spriteBufferPosition = targetBufferPosition - Displacement { CalculateWidth2(sprite ? sprite->Width() : 96), 0 }; - int frames = SDL_SwapLE32(*reinterpret_cast(sprite->Data())); - if (nCel < 1 || frames > 50 || nCel > frames) { + const uint32_t frames = LoadLE32(sprite->Data()); + if (nCel < 0 || frames > 50 || nCel >= static_cast(frames)) { const char *szMode = "unknown action"; if (player._pmode <= PM_QUIT) szMode = PlayerModeNames[player._pmode]; @@ -622,21 +622,21 @@ void DrawObject(const Surface &out, Point tilePosition, Point targetBufferPositi return; } - uint32_t nCel = objectToDraw._oAnimFrame; - uint32_t frames = LoadLE32(pCelBuff); - if (nCel < 1 || frames > 50 || nCel > frames) { + const uint32_t nCel = objectToDraw._oAnimFrame - 1; + const uint32_t frames = LoadLE32(pCelBuff); + if (nCel == static_cast(-1) || frames > 50 || nCel >= frames) { Log("Draw Object: frame {} of {}, object type=={}", nCel, frames, objectToDraw._otype); return; } CelSprite cel { objectToDraw._oAnimData, objectToDraw._oAnimWidth }; if (pcursobj != -1 && &objectToDraw == &Objects[pcursobj]) { - CelBlitOutlineTo(out, 194, screenPosition, cel, objectToDraw._oAnimFrame); + CelBlitOutlineTo(out, 194, screenPosition, cel, nCel); } if (objectToDraw._oLight) { - CelClippedDrawLightTo(out, screenPosition, cel, objectToDraw._oAnimFrame); + CelClippedDrawLightTo(out, screenPosition, cel, nCel); } else { - CelClippedDrawTo(out, screenPosition, cel, objectToDraw._oAnimFrame); + CelClippedDrawTo(out, screenPosition, cel, nCel); } } @@ -719,7 +719,7 @@ void DrawItem(const Surface &out, Point tilePosition, Point targetBufferPosition int nCel = item.AnimInfo.GetFrameToUseForRendering(); const uint32_t frames = LoadLE32(cel->Data()); - if (nCel < 1 || frames > 50 || nCel > static_cast(frames)) { + if (nCel < 0 || frames > 50 || nCel >= static_cast(frames)) { Log("Draw \"{}\" Item 1: frame {} of {}, item type=={}", item._iIName, nCel, frames, ItemTypeToString(item._itype)); return; } @@ -730,7 +730,7 @@ void DrawItem(const Surface &out, Point tilePosition, Point targetBufferPosition CelBlitOutlineTo(out, GetOutlineColor(item, false), position, *cel, nCel); } CelClippedDrawLightTo(out, position, *cel, nCel); - if (item.AnimInfo.CurrentFrame == item.AnimInfo.NumberOfFrames || item._iCurs == ICURS_MAGIC_ROCK) + if (item.AnimInfo.CurrentFrame == item.AnimInfo.NumberOfFrames - 1 || item._iCurs == ICURS_MAGIC_ROCK) AddItemToLabelQueue(bItem - 1, px, targetBufferPosition.y); } @@ -853,8 +853,8 @@ void DrawDungeon(const Surface &out, Point tilePosition, Point targetBufferPosit const byte *pCelBuff = pDeadGuy->data[(bDead >> 5) & 7]; assert(pCelBuff != nullptr); const uint32_t frames = LoadLE32(pCelBuff); - int nCel = pDeadGuy->frame; - if (nCel < 1 || frames > 50 || nCel > static_cast(frames)) { + const int nCel = pDeadGuy->frame; + if (nCel < 0 || frames >= 50 || nCel > static_cast(frames)) { Log("Unclipped dead: frame {} of {}, deadnum=={}", nCel, frames, (bDead & 0x1F) - 1); break; } @@ -905,7 +905,7 @@ void DrawDungeon(const Surface &out, Point tilePosition, Point targetBufferPosit if (tilePosition.x > 0 && tilePosition.y > 0 && targetBufferPosition.y > TILE_HEIGHT) { char bArch = dSpecial[tilePosition.x - 1][tilePosition.y - 1]; if (bArch != 0) { - CelDrawTo(out, targetBufferPosition + Displacement { 0, -TILE_HEIGHT }, *pSpecialCels, bArch); + CelDrawTo(out, targetBufferPosition + Displacement { 0, -TILE_HEIGHT }, *pSpecialCels, bArch - 1); } } } diff --git a/Source/stores.cpp b/Source/stores.cpp index c9adf4ebc..0a4b45dbf 100644 --- a/Source/stores.cpp +++ b/Source/stores.cpp @@ -159,7 +159,7 @@ void CalculateLineHeights() void DrawSTextBack(const Surface &out) { - CelDrawTo(out, { PANEL_X + 320 + 24, 327 + UI_OFFSET_Y }, *pSTextBoxCels, 1); + CelDrawTo(out, { PANEL_X + 320 + 24, 327 + UI_OFFSET_Y }, *pSTextBoxCels, 0); DrawHalfTransparentRectTo(out, PANEL_X + 347, UI_OFFSET_Y + 28, 265, 297); } @@ -168,17 +168,17 @@ void DrawSSlider(const Surface &out, int y1, int y2) int yd1 = y1 * 12 + 44 + UI_OFFSET_Y; int yd2 = y2 * 12 + 44 + UI_OFFSET_Y; if (stextscrlubtn != -1) - CelDrawTo(out, { PANEL_X + 601, yd1 }, *pSTextSlidCels, 12); + CelDrawTo(out, { PANEL_X + 601, yd1 }, *pSTextSlidCels, 11); else - CelDrawTo(out, { PANEL_X + 601, yd1 }, *pSTextSlidCels, 10); + CelDrawTo(out, { PANEL_X + 601, yd1 }, *pSTextSlidCels, 9); if (stextscrldbtn != -1) - CelDrawTo(out, { PANEL_X + 601, yd2 }, *pSTextSlidCels, 11); + CelDrawTo(out, { PANEL_X + 601, yd2 }, *pSTextSlidCels, 10); else - CelDrawTo(out, { PANEL_X + 601, yd2 }, *pSTextSlidCels, 9); + CelDrawTo(out, { PANEL_X + 601, yd2 }, *pSTextSlidCels, 8); yd1 += 12; int yd3 = yd1; for (; yd3 < yd2; yd3 += 12) { - CelDrawTo(out, { PANEL_X + 601, yd3 }, *pSTextSlidCels, 14); + CelDrawTo(out, { PANEL_X + 601, yd3 }, *pSTextSlidCels, 13); } if (stextsel == BackButtonLine()) yd3 = stextlhold; @@ -188,7 +188,7 @@ void DrawSSlider(const Surface &out, int y1, int y2) yd3 = 1000 * (stextsval + ((yd3 - stextup) / 4)) / (storenumh - 1) * (y2 * 12 - y1 * 12 - 24) / 1000; else yd3 = 0; - CelDrawTo(out, { PANEL_X + 601, (y1 + 1) * 12 + 44 + UI_OFFSET_Y + yd3 }, *pSTextSlidCels, 13); + CelDrawTo(out, { PANEL_X + 601, (y1 + 1) * 12 + 44 + UI_OFFSET_Y + yd3 }, *pSTextSlidCels, 12); } void AddSLine(int y) diff --git a/Source/towners.cpp b/Source/towners.cpp index e9b3cb42a..2fcff2209 100644 --- a/Source/towners.cpp +++ b/Source/towners.cpp @@ -31,7 +31,7 @@ void NewTownerAnim(Towner &towner, byte *pAnim, uint8_t numFrames, int delay) { towner._tAnimData = pAnim; towner._tAnimLen = numFrames; - towner._tAnimFrame = 1; + towner._tAnimFrame = 0; towner._tAnimCnt = 0; towner._tAnimDelay = delay; } @@ -64,13 +64,13 @@ void InitSmith(Towner &towner, const TownerData &townerData) towner._tAnimWidth = 96; static const uint8_t AnimOrder[] = { // clang-format off - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4 + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 // clang-format on }; towner.animOrder = AnimOrder; @@ -84,15 +84,15 @@ void InitBarOwner(Towner &towner, const TownerData &townerData) towner._tAnimWidth = 96; static const uint8_t AnimOrder[] = { // clang-format off - 1, 2, 3, 3, 2, 1, 16, 15, 14, 14, 15, 16, - 1, 2, 3, 3, 2, 1, 16, 15, 14, 14, 15, 16, - 1, 2, 3, 3, 2, 1, 16, 15, 14, 14, 15, 16, - 1, 2, 3, 3, 2, 1, 16, 15, 14, 14, 15, 16, - 1, 2, 3, 3, 2, 1, 16, 15, 14, 14, 15, 16, - 1, 2, 3, 3, 2, 1, 16, 15, 14, 14, 15, 16, - 1, 2, 3, 3, 2, 1, 16, 15, 14, 14, 15, 16, - 1, 2, 3, 2, 1, 16, 15, 14, 14, 15, 16, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + 0, 1, 2, 2, 1, 0, 15, 14, 13, 13, 14, 15, + 0, 1, 2, 2, 1, 0, 15, 14, 13, 13, 14, 15, + 0, 1, 2, 2, 1, 0, 15, 14, 13, 13, 14, 15, + 0, 1, 2, 2, 1, 0, 15, 14, 13, 13, 14, 15, + 0, 1, 2, 2, 1, 0, 15, 14, 13, 13, 14, 15, + 0, 1, 2, 2, 1, 0, 15, 14, 13, 13, 14, 15, + 0, 1, 2, 2, 1, 0, 15, 14, 13, 13, 14, 15, + 0, 1, 2, 1, 0, 15, 14, 13, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 // clang-format on }; towner.animOrder = AnimOrder; @@ -115,15 +115,15 @@ void InitWitch(Towner &towner, const TownerData &townerData) towner._tAnimWidth = 96; static const uint8_t AnimOrder[] = { // clang-format off - 4, 4, 4, 5, 6, 6, 6, 5, 4, 15, 14, 13, 13, 13, 14, 15, 4, 5, 6, 6, 6, 5, - 4, 4, 4, 5, 6, 6, 6, 5, 4, 15, 14, 13, 13, 13, 14, 15, 4, 5, 6, 6, 6, 5, - 4, 4, 4, 5, 6, 6, 6, 5, 4, 15, 14, 13, 13, 13, 14, 15, 4, 5, 6, 6, 6, 5, - 4, 3, 2, 1, 19, 18, 19, 1, 2, 1, 19, 18, 19, 1, 2, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 15, 15, 14, 13, 13, 13, 13, 14, 15, - 15, 15, 14, 13, 12, 12, 12, 11, 10, 10, 10, 9, - 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 1, 2, 1, 19, 18, 19, 1, 2, 1, 2, 3 + 3, 3, 3, 4, 5, 5, 5, 4, 3, 14, 13, 12, 12, 12, 13, 14, 3, 4, 5, 5, 5, 4, + 3, 3, 3, 4, 5, 5, 5, 4, 3, 14, 13, 12, 12, 12, 13, 14, 3, 4, 5, 5, 5, 4, + 3, 3, 3, 4, 5, 5, 5, 4, 3, 14, 13, 12, 12, 12, 13, 14, 3, 4, 5, 5, 5, 4, + 3, 2, 1, 0, 18, 17, 18, 0, 1, 0, 18, 17, 18, 0, 1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 14, 14, 13, 12, 12, 12, 12, 13, 14, + 14, 14, 13, 12, 11, 11, 11, 10, 9, 9, 9, 8, + 7, 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 0, 1, 0, 18, 17, 18, 0, 1, 0, 1, 2 // clang-format on }; towner.animOrder = AnimOrder; @@ -155,15 +155,15 @@ void InitHealer(Towner &towner, const TownerData &townerData) towner._tAnimWidth = 96; static const uint8_t AnimOrder[] = { // clang-format off - 1, 2, 3, 3, 2, 1, 20, 19, 19, 20, - 1, 2, 3, 3, 2, 1, 20, 19, 19, 20, - 1, 2, 3, 3, 2, 1, 20, 19, 19, 20, - 1, 2, 3, 3, 2, 1, 20, 19, 19, 20, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + 0, 1, 2, 2, 1, 0, 19, 18, 18, 19, + 0, 1, 2, 2, 1, 0, 19, 18, 18, 19, + 0, 1, 2, 2, 1, 0, 19, 18, 18, 19, + 0, 1, 2, 2, 1, 0, 19, 18, 18, 19, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 // clang-format on }; towner.animOrder = AnimOrder; @@ -177,10 +177,10 @@ void InitTeller(Towner &towner, const TownerData &townerData) towner._tAnimWidth = 96; static const uint8_t AnimOrder[] = { // clang-format off - 1, 1, 25, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 1, 1, 1, 25, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + 0, 0, 24, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 24, 24, 0, 0, 0, 24, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 // clang-format on }; towner.animOrder = AnimOrder; @@ -194,9 +194,9 @@ void InitDrunk(Towner &towner, const TownerData &townerData) towner._tAnimWidth = 96; static const uint8_t AnimOrder[] = { // clang-format off - 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11, 12, 13, 14, 15, 16, 17, 18, 18, - 1, 1, 1, 18, 17, 16, 15, 14, 13, 12, 11, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 1, 2, 3, 4, 5, 5, 5, 4, 3, 2 + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11, 12, 13, 14, 15, 16, 17, 17, + 0, 0, 0, 17, 16, 15, 14, 13, 12, 11, 10, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 0, 1, 2, 3, 4, 4, 4, 3, 2, 1 // clang-format on }; towner.animOrder = AnimOrder; @@ -210,11 +210,9 @@ void InitCows(Towner &towner, const TownerData &townerData) towner._tAnimWidth = 128; towner.animOrder = nullptr; towner.animOrderSize = 0; - for (int i = 0; i < 8; i++) { - towner._tNAnim[i] = CelGetFrame(CowCels.get(), i); - } + CelGetDirectionFrames(CowCels.get(), towner._tNAnim); NewTownerAnim(towner, towner._tNAnim[static_cast(townerData.dir)], 12, 3); - towner._tAnimFrame = GenerateRnd(11) + 1; + towner._tAnimFrame = GenerateRnd(11); towner.name = _("Cow"); const Point position = townerData.position; @@ -650,7 +648,7 @@ void TalkToCowFarmer(Player &player, Towner &cowFarmer) quest._qactive = QUEST_DONE; auto curFrame = cowFarmer._tAnimFrame; LoadTownerAnimations(cowFarmer, "Towners\\Farmer\\mfrmrn2.CEL", 15, 3); - cowFarmer._tAnimFrame = std::min(curFrame, cowFarmer._tAnimLen); + cowFarmer._tAnimFrame = std::min(curFrame, cowFarmer._tAnimLen - 1); return; } @@ -730,7 +728,7 @@ void TalkToGirl(Player &player, Towner &girl) quest._qactive = QUEST_DONE; auto curFrame = girl._tAnimFrame; LoadTownerAnimations(girl, "Towners\\Girl\\Girls1.CEL", 20, 6); - girl._tAnimFrame = std::min(curFrame, girl._tAnimLen); + girl._tAnimFrame = std::min(curFrame, girl._tAnimLen - 1); if (gbIsMultiplayer) NetSendCmdQuest(true, quest); return; @@ -866,8 +864,8 @@ void ProcessTowners() } towner._tAnimFrame++; - if (towner._tAnimFrame > towner._tAnimLen) - towner._tAnimFrame = 1; + if (towner._tAnimFrame >= towner._tAnimLen) + towner._tAnimFrame = 0; } } diff --git a/test/animationinfo_test.cpp b/test/animationinfo_test.cpp index 8ab51a4cc..8ede00e61 100644 --- a/test/animationinfo_test.cpp +++ b/test/animationinfo_test.cpp @@ -108,11 +108,16 @@ TEST(AnimationInfo, AttackSwordWarrior) // ProcessAnimationPending should be con { new SetNewAnimationData(16, 1, AnimationDistributionFlags::ProcessAnimationPending, 0, 9), // ProcessAnimation directly after StartAttack (in same GameTick). So we don't see any rendering before. + new GameTickData(1, 0), + new RenderingData(0.0f, 0), + new RenderingData(0.3f, 0), + new RenderingData(0.6f, 0), + new RenderingData(0.8f, 0), new GameTickData(2, 0), new RenderingData(0.0f, 1), new RenderingData(0.3f, 1), new RenderingData(0.6f, 1), - new RenderingData(0.8f, 1), + new RenderingData(0.8f, 2), new GameTickData(3, 0), new RenderingData(0.0f, 2), new RenderingData(0.3f, 2), @@ -121,7 +126,7 @@ TEST(AnimationInfo, AttackSwordWarrior) // ProcessAnimationPending should be con new GameTickData(4, 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), @@ -130,7 +135,7 @@ TEST(AnimationInfo, AttackSwordWarrior) // ProcessAnimationPending should be con new RenderingData(0.8f, 5), new GameTickData(6, 0), new RenderingData(0.0f, 5), - new RenderingData(0.3f, 5), + new RenderingData(0.3f, 6), new RenderingData(0.6f, 6), new RenderingData(0.8f, 6), new GameTickData(7, 0), @@ -138,30 +143,25 @@ TEST(AnimationInfo, AttackSwordWarrior) // ProcessAnimationPending should be con 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), // After this GameTick, the Animation Distribution Logic is disabled + new GameTickData(8, 0), + new RenderingData(0.1f, 8), new GameTickData(9, 0), - new RenderingData(0.1f, 9), + new RenderingData(0.4f, 9), new GameTickData(10, 0), new RenderingData(0.4f, 10), new GameTickData(11, 0), - new RenderingData(0.4f, 11), + new RenderingData(0.3f, 11), new GameTickData(12, 0), - new RenderingData(0.3f, 12), + new RenderingData(0.0f, 12), new GameTickData(13, 0), - new RenderingData(0.0f, 13), + new RenderingData(0.6f, 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].AnimInfo.CurrentFrame == plr[pnum]._pAFrames) {" + // Animation stopped cause PM_DoAttack would stop the Animation "if (plr[pnum].AnimInfo.CurrentFrame == plr[pnum]._pAFrames - 1) {" }); } @@ -171,50 +171,50 @@ TEST(AnimationInfo, AttackSwordWarriorWithFastestAttack) // Skipped frames and P { new SetNewAnimationData(16, 1, AnimationDistributionFlags::ProcessAnimationPending, 2, 9), // ProcessAnimation directly after StartAttack (in same GameTick). So we don't see any rendering before. + new GameTickData(3, 0), + new RenderingData(0.0f, 0), + new RenderingData(0.3f, 0), + new RenderingData(0.6f, 0), + new RenderingData(0.8f, 1), new GameTickData(4, 0), new RenderingData(0.0f, 1), - new RenderingData(0.3f, 1), - new RenderingData(0.6f, 1), + new RenderingData(0.3f, 2), + new RenderingData(0.6f, 2), new RenderingData(0.8f, 2), new GameTickData(5, 0), - new RenderingData(0.0f, 2), + new RenderingData(0.0f, 3), new RenderingData(0.3f, 3), - new RenderingData(0.6f, 3), - new RenderingData(0.8f, 3), + new RenderingData(0.6f, 4), + new RenderingData(0.8f, 4), new GameTickData(6, 0), new RenderingData(0.0f, 4), - new RenderingData(0.3f, 4), + new RenderingData(0.3f, 5), new RenderingData(0.6f, 5), - new RenderingData(0.8f, 5), + new RenderingData(0.8f, 6), new GameTickData(7, 0), - new RenderingData(0.0f, 5), + new RenderingData(0.0f, 6), new RenderingData(0.3f, 6), - new RenderingData(0.6f, 6), + new RenderingData(0.6f, 7), 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), // After this GameTick, the Animation Distribution Logic is disabled + new GameTickData(8, 0), + new RenderingData(0.1f, 8), new GameTickData(9, 0), - new RenderingData(0.1f, 9), + new RenderingData(0.4f, 9), new GameTickData(10, 0), new RenderingData(0.4f, 10), new GameTickData(11, 0), - new RenderingData(0.4f, 11), + new RenderingData(0.3f, 11), new GameTickData(12, 0), - new RenderingData(0.3f, 12), + new RenderingData(0.0f, 12), new GameTickData(13, 0), - new RenderingData(0.0f, 13), + new RenderingData(0.6f, 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].AnimInfo.CurrentFrame == plr[pnum]._pAFrames) {" + // Animation stopped cause PM_DoAttack would stop the Animation "if (plr[pnum].AnimInfo.CurrentFrame == plr[pnum]._pAFrames - 1) {" }); } @@ -227,11 +227,16 @@ TEST(AnimationInfo, AttackSwordWarriorRepeated) { new SetNewAnimationData(16, 1, AnimationDistributionFlags::ProcessAnimationPending, 0, 9), // ProcessAnimation directly after StartAttack (in same GameTick). So we don't see any rendering before. + new GameTickData(1, 0), + new RenderingData(0.0f, 0), + new RenderingData(0.3f, 0), + new RenderingData(0.6f, 0), + new RenderingData(0.8f, 0), new GameTickData(2, 0), new RenderingData(0.0f, 1), new RenderingData(0.3f, 1), new RenderingData(0.6f, 1), - new RenderingData(0.8f, 1), + new RenderingData(0.8f, 2), new GameTickData(3, 0), new RenderingData(0.0f, 2), new RenderingData(0.3f, 2), @@ -240,7 +245,7 @@ TEST(AnimationInfo, AttackSwordWarriorRepeated) new GameTickData(4, 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), @@ -249,7 +254,7 @@ TEST(AnimationInfo, AttackSwordWarriorRepeated) new RenderingData(0.8f, 5), new GameTickData(6, 0), new RenderingData(0.0f, 5), - new RenderingData(0.3f, 5), + new RenderingData(0.3f, 6), new RenderingData(0.6f, 6), new RenderingData(0.8f, 6), new GameTickData(7, 0), @@ -257,75 +262,70 @@ TEST(AnimationInfo, AttackSwordWarriorRepeated) 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), // After this GameTick, the Animation Distribution Logic is disabled + new GameTickData(8, 0), + new RenderingData(0.1f, 8), new GameTickData(9, 0), - new RenderingData(0.1f, 9), - new GameTickData(10, 0), - new RenderingData(0.3f, 10), + new RenderingData(0.3f, 9), // Start of repeated attack, cause plr[pnum].AnimInfo.CurrentFrame > plr[myplr]._pAFNum new SetNewAnimationData(16, 1, static_cast(AnimationDistributionFlags::ProcessAnimationPending | AnimationDistributionFlags::RepeatedAction), 0, 9), // ProcessAnimation directly after StartAttack (in same GameTick). So we don't see any rendering before. + new GameTickData(1, 0), + new RenderingData(0.0f, 10), + new RenderingData(0.3f, 10), + new RenderingData(0.6f, 11), + new RenderingData(0.8f, 11), 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 RenderingData(0.0f, 12), + new RenderingData(0.3f, 12), + new RenderingData(0.6f, 13), + new RenderingData(0.8f, 13), 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 RenderingData(0.0f, 14), + new RenderingData(0.3f, 14), + new RenderingData(0.6f, 15), + new RenderingData(0.8f, 15), 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 RenderingData(0.0f, 0), + new RenderingData(0.3f, 0), + new RenderingData(0.6f, 1), + new RenderingData(0.8f, 1), new GameTickData(5, 0), - new RenderingData(0.0f, 1), - new RenderingData(0.3f, 1), - new RenderingData(0.6f, 2), - new RenderingData(0.8f, 2), + new RenderingData(0.0f, 2), + new RenderingData(0.3f, 2), + new RenderingData(0.6f, 3), + new RenderingData(0.8f, 3), new GameTickData(6, 0), - new RenderingData(0.0f, 3), - new RenderingData(0.3f, 3), - new RenderingData(0.6f, 4), - new RenderingData(0.8f, 4), + new RenderingData(0.0f, 4), + new RenderingData(0.3f, 4), + new RenderingData(0.6f, 5), + new RenderingData(0.8f, 5), 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(8, 0), - new RenderingData(0.0f, 7), - new RenderingData(0.3f, 7), - new RenderingData(0.6f, 8), - new RenderingData(0.8f, 8), + new RenderingData(0.0f, 6), + new RenderingData(0.3f, 6), + new RenderingData(0.6f, 7), + new RenderingData(0.8f, 7), // After this GameTick, the Animation Distribution Logic is disabled + new GameTickData(8, 0), + new RenderingData(0.1f, 8), new GameTickData(9, 0), - new RenderingData(0.1f, 9), + new RenderingData(0.4f, 9), new GameTickData(10, 0), new RenderingData(0.4f, 10), new GameTickData(11, 0), - new RenderingData(0.4f, 11), + new RenderingData(0.3f, 11), new GameTickData(12, 0), - new RenderingData(0.3f, 12), + new RenderingData(0.0f, 12), new GameTickData(13, 0), - new RenderingData(0.0f, 13), + new RenderingData(0.6f, 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].AnimInfo.CurrentFrame == plr[pnum]._pAFrames) {" + // Animation stopped cause PM_DoAttack would stop the Animation "if (plr[pnum].AnimInfo.CurrentFrame == plr[pnum]._pAFrames - 1) {" }); } @@ -334,25 +334,25 @@ TEST(AnimationInfo, BlockingWarriorNormal) // Ignored delay for last Frame shoul RunAnimationTest( { new SetNewAnimationData(2, 3, AnimationDistributionFlags::SkipsDelayOfLastFrame), + new RenderingData(0.0f, 0), + new RenderingData(0.3f, 0), + new RenderingData(0.6f, 0), + new RenderingData(0.8f, 0), + new GameTickData(0, 1), + new RenderingData(0.0f, 0), + new RenderingData(0.3f, 0), + new RenderingData(0.6f, 0), + new RenderingData(0.8f, 0), + new GameTickData(0, 2), 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 GameTickData(1, 0), 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].AnimInfo.CurrentFrame >= plr[pnum]._pBFrames) {" }); } @@ -362,25 +362,25 @@ TEST(AnimationInfo, BlockingSorcererWithFastBlock) // Skipped frames and ignored RunAnimationTest( { new SetNewAnimationData(6, 3, AnimationDistributionFlags::SkipsDelayOfLastFrame, 4), + new RenderingData(0.0f, 0), + new RenderingData(0.3f, 0), + new RenderingData(0.6f, 0), + new RenderingData(0.8f, 1), + new GameTickData(4, 1), new RenderingData(0.0f, 1), new RenderingData(0.3f, 1), - new RenderingData(0.6f, 1), + new RenderingData(0.6f, 2), new RenderingData(0.8f, 2), - new GameTickData(5, 1), - new RenderingData(0.0f, 2), - new RenderingData(0.3f, 2), + new GameTickData(4, 2), + new RenderingData(0.0f, 3), + new RenderingData(0.3f, 3), new RenderingData(0.6f, 3), - new RenderingData(0.8f, 3), - new GameTickData(5, 2), + new RenderingData(0.8f, 4), + new GameTickData(5, 0), new RenderingData(0.0f, 4), new RenderingData(0.3f, 4), - new RenderingData(0.6f, 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), // Animation stopped cause PM_DoBlock would stop the Animation "if (plr[pnum].AnimInfo.CurrentFrame >= plr[pnum]._pBFrames) {" }); } @@ -390,25 +390,25 @@ TEST(AnimationInfo, HitRecoverySorcererZenMode) // Skipped frames and ignored de RunAnimationTest( { new SetNewAnimationData(8, 1, AnimationDistributionFlags::None, 4), - new RenderingData(0.0f, 1), - new RenderingData(0.3f, 1), - new RenderingData(0.6f, 2), - new RenderingData(0.8f, 2), + new RenderingData(0.0f, 0), + new RenderingData(0.3f, 0), + new RenderingData(0.6f, 1), + new RenderingData(0.8f, 1), + new GameTickData(5, 0), + new RenderingData(0.0f, 2), + new RenderingData(0.3f, 2), + new RenderingData(0.6f, 3), + new RenderingData(0.8f, 3), new GameTickData(6, 0), - new RenderingData(0.0f, 3), - new RenderingData(0.3f, 3), - new RenderingData(0.6f, 4), - new RenderingData(0.8f, 4), + new RenderingData(0.0f, 4), + new RenderingData(0.3f, 4), + new RenderingData(0.6f, 5), + new RenderingData(0.8f, 5), 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(8, 0), - new RenderingData(0.0f, 7), - new RenderingData(0.3f, 7), - new RenderingData(0.6f, 8), - new RenderingData(0.8f, 8), + new RenderingData(0.0f, 6), + new RenderingData(0.3f, 6), + new RenderingData(0.6f, 7), + new RenderingData(0.8f, 7), // Animation stopped cause PM_DoGotHit would stop the Animation "if (plr[pnum].AnimInfo.CurrentFrame >= plr[pnum]._pHFrames) {" }); } @@ -417,7 +417,16 @@ TEST(AnimationInfo, Stand) // Distribution Logic shouldn't change anything here RunAnimationTest( { new SetNewAnimationData(10, 4), - new RenderingData(0.1f, 1), + new RenderingData(0.1f, 0), + new GameTickData(0, 1), + new RenderingData(0.6f, 0), + new GameTickData(0, 2), + new RenderingData(0.6f, 0), + new GameTickData(0, 3), + new RenderingData(0.6f, 0), + + new GameTickData(1, 0), + new RenderingData(0.6f, 1), new GameTickData(1, 1), new RenderingData(0.6f, 1), new GameTickData(1, 2), @@ -497,23 +506,14 @@ TEST(AnimationInfo, Stand) // Distribution Logic shouldn't change anything here 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), - // Animation starts again - new GameTickData(1, 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(0, 0), + new RenderingData(0.1f, 0), + new GameTickData(0, 1), + new RenderingData(0.6f, 0), + new GameTickData(0, 2), + new RenderingData(0.6f, 0), + new GameTickData(0, 3), + new RenderingData(0.6f, 0), }); } diff --git a/test/writehero_test.cpp b/test/writehero_test.cpp index bf34c4910..6986ca0c5 100644 --- a/test/writehero_test.cpp +++ b/test/writehero_test.cpp @@ -267,7 +267,7 @@ static void AssertPlayer(Player &player) ASSERT_EQ(player.AnimInfo.TicksPerFrame, 4); ASSERT_EQ(player.AnimInfo.TickCounterOfCurrentFrame, 1); ASSERT_EQ(player.AnimInfo.NumberOfFrames, 20); - ASSERT_EQ(player.AnimInfo.CurrentFrame, 1); + ASSERT_EQ(player.AnimInfo.CurrentFrame, 0); ASSERT_EQ(player._pSpell, -1); ASSERT_EQ(player._pSplType, 4); ASSERT_EQ(player._pSplFrom, 0);