1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3
4#include <base/math.h>
5#include <game/generated/client_data.h>
6
7#include "animstate.h"
8
9static void AnimSeqEval(const CAnimSequence *pSeq, float Time, CAnimKeyframe *pFrame)
10{
11 if(pSeq->m_NumFrames == 0)
12 {
13 pFrame->m_Time = 0;
14 pFrame->m_X = 0;
15 pFrame->m_Y = 0;
16 pFrame->m_Angle = 0;
17 }
18 else if(pSeq->m_NumFrames == 1)
19 {
20 *pFrame = pSeq->m_aFrames[0];
21 }
22 else
23 {
24 CAnimKeyframe *pFrame1 = nullptr;
25 CAnimKeyframe *pFrame2 = nullptr;
26 float Blend = 0.0f;
27
28 // TODO: make this smarter.. binary search
29 for(int i = 1; i < pSeq->m_NumFrames; i++)
30 {
31 if(pSeq->m_aFrames[i - 1].m_Time <= Time && pSeq->m_aFrames[i].m_Time >= Time)
32 {
33 pFrame1 = &pSeq->m_aFrames[i - 1];
34 pFrame2 = &pSeq->m_aFrames[i];
35 Blend = (Time - pFrame1->m_Time) / (pFrame2->m_Time - pFrame1->m_Time);
36 break;
37 }
38 }
39
40 if(pFrame1 != nullptr && pFrame2 != nullptr)
41 {
42 pFrame->m_Time = Time;
43 pFrame->m_X = mix(a: pFrame1->m_X, b: pFrame2->m_X, amount: Blend);
44 pFrame->m_Y = mix(a: pFrame1->m_Y, b: pFrame2->m_Y, amount: Blend);
45 pFrame->m_Angle = mix(a: pFrame1->m_Angle, b: pFrame2->m_Angle, amount: Blend);
46 }
47 }
48}
49
50static void AnimAddKeyframe(CAnimKeyframe *pSeq, const CAnimKeyframe *pAdded, float Amount)
51{
52 // AnimSeqEval fills m_X for any case, clang-analyzer assumes going into the
53 // final else branch with pSeq->m_NumFrames < 2, which is impossible.
54 pSeq->m_X += pAdded->m_X * Amount; // NOLINT(clang-analyzer-core.UndefinedBinaryOperatorResult)
55 pSeq->m_Y += pAdded->m_Y * Amount;
56 pSeq->m_Angle += pAdded->m_Angle * Amount;
57}
58
59void CAnimState::AnimAdd(CAnimState *pState, const CAnimState *pAdded, float Amount)
60{
61 AnimAddKeyframe(pSeq: &pState->m_Body, pAdded: pAdded->GetBody(), Amount);
62 AnimAddKeyframe(pSeq: &pState->m_BackFoot, pAdded: pAdded->GetBackFoot(), Amount);
63 AnimAddKeyframe(pSeq: &pState->m_FrontFoot, pAdded: pAdded->GetFrontFoot(), Amount);
64 AnimAddKeyframe(pSeq: &pState->m_Attach, pAdded: pAdded->GetAttach(), Amount);
65}
66
67void CAnimState::Set(CAnimation *pAnim, float Time)
68{
69 AnimSeqEval(pSeq: &pAnim->m_Body, Time, pFrame: &m_Body);
70 AnimSeqEval(pSeq: &pAnim->m_BackFoot, Time, pFrame: &m_BackFoot);
71 AnimSeqEval(pSeq: &pAnim->m_FrontFoot, Time, pFrame: &m_FrontFoot);
72 AnimSeqEval(pSeq: &pAnim->m_Attach, Time, pFrame: &m_Attach);
73}
74
75void CAnimState::Add(CAnimation *pAnim, float Time, float Amount)
76{
77 CAnimState Add;
78 Add.Set(pAnim, Time);
79 AnimAdd(pState: this, pAdded: &Add, Amount);
80}
81
82const CAnimState *CAnimState::GetIdle()
83{
84 static CAnimState s_State;
85 static bool s_Init = true;
86
87 if(s_Init)
88 {
89 s_State.Set(pAnim: &g_pData->m_aAnimations[ANIM_BASE], Time: 0.0f);
90 s_State.Add(pAnim: &g_pData->m_aAnimations[ANIM_IDLE], Time: 0.0f, Amount: 1.0f);
91 s_Init = false;
92 }
93
94 return &s_State;
95}
96