Otclient  14/8/2020
animator.cpp
Go to the documentation of this file.
1 /*
2 * Copyright (c) 2010-2020 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22 
23 #include "declarations.h"
24 #include "animator.h"
25 
26 #include <framework/core/clock.h>
28 
30 {
31  m_animationPhases = 0;
32  m_startPhase = 0;
33  m_loopCount = 0;
34  m_async = false;
35  m_currentDuration = 0;
36  m_currentDirection = AnimDirForward;
37  m_currentLoop = 0;
38  m_lastPhaseTicks = 0;
39  m_isComplete = false;
40  m_phase = 0;
41 }
42 
43 void Animator::unserialize(int animationPhases, const FileStreamPtr& fin)
44 {
45  m_animationPhases = animationPhases;
46  m_async = fin->getU8() == 0;
47  m_loopCount = fin->get32();
48  m_startPhase = fin->get8();
49 
50  for(int i = 0; i < m_animationPhases; ++i) {
51  int minimum = fin->getU32();
52  int maximum = fin->getU32();
53  m_phaseDurations.emplace_back(minimum, maximum);
54  }
55 
56  m_phase = getStartPhase();
57 
58  assert(m_animationPhases == (int)m_phaseDurations.size());
59  assert(m_startPhase >= -1 && m_startPhase < m_animationPhases);
60 }
61 
63 {
64  fin->addU8(m_async ? 0 : 1);
65  fin->add32(m_loopCount);
66  fin->add8(m_startPhase);
67 
68  for(std::tuple<int, int> phase : m_phaseDurations) {
69  fin->addU32(std::get<0>(phase));
70  fin->addU32(std::get<1>(phase));
71  }
72 }
73 
74 void Animator::setPhase(int phase)
75 {
76  if(m_phase == phase) return;
77 
78  if(m_async) {
79  if(phase == AnimPhaseAsync)
80  m_phase = 0;
81  else if(phase == AnimPhaseRandom)
82  m_phase = (int)stdext::random_range(0, (long)m_animationPhases);
83  else if(phase >= 0 && phase < m_animationPhases)
84  m_phase = phase;
85  else
86  m_phase = getStartPhase();
87 
88  m_isComplete = false;
89  m_lastPhaseTicks = g_clock.millis();
90  m_currentDuration = getPhaseDuration(phase);
91  m_currentLoop = 0;
92  } else
93  calculateSynchronous();
94 }
95 
97 {
98  ticks_t ticks = g_clock.millis();
99  if(ticks != m_lastPhaseTicks && !m_isComplete) {
100  int elapsedTicks = (int)(ticks - m_lastPhaseTicks);
101  if(elapsedTicks >= m_currentDuration) {
102  int phase = 0;
103  if(m_loopCount < 0)
104  phase = getPingPongPhase();
105  else
106  phase = getLoopPhase();
107 
108  if(m_phase != phase) {
109  int duration = getPhaseDuration(phase) - (elapsedTicks - m_currentDuration);
110  if(duration < 0 && !m_async) {
111  calculateSynchronous();
112  } else {
113  m_phase = phase;
114  m_currentDuration = std::max<int>(0, duration);
115  }
116  } else
117  m_isComplete = true;
118  } else
119  m_currentDuration -= elapsedTicks;
120 
121  m_lastPhaseTicks = ticks;
122  }
123  return m_phase;
124 }
125 
127 {
128  int index = 0;
129  ticks_t total = 0;
130 
131  for(const auto &pair: m_phaseDurations) {
132  total += std::get<1>(pair);
133 
134  if (time < total) {
135  return index;
136  }
137 
138  ++index;
139  }
140 
141  return std::min<int>(index, m_animationPhases - 1);
142 }
143 
145 {
146  if(m_startPhase > -1)
147  return m_startPhase;
148  return (int)stdext::random_range(0, (long)m_animationPhases);
149 }
150 
152 {
153  m_isComplete = false;
154  m_currentDirection = AnimDirForward;
155  m_currentLoop = 0;
157 }
158 
159 int Animator::getPingPongPhase()
160 {
161  int count = m_currentDirection == AnimDirForward ? 1 : -1;
162  int nextPhase = m_phase + count;
163  if(nextPhase < 0 || nextPhase >= m_animationPhases) {
164  m_currentDirection = m_currentDirection == AnimDirForward ? AnimDirBackward : AnimDirForward;
165  count *= -1;
166  }
167  return m_phase + count;
168 }
169 
170 int Animator::getLoopPhase()
171 {
172  int nextPhase = m_phase + 1;
173  if(nextPhase < m_animationPhases)
174  return nextPhase;
175 
176  if(m_loopCount == 0)
177  return 0;
178 
179  if(m_currentLoop < (m_loopCount - 1)) {
180  m_currentLoop++;
181  return 0;
182  }
183 
184  return m_phase;
185 }
186 
187 int Animator::getPhaseDuration(int phase)
188 {
189  assert(phase < (int)m_phaseDurations.size());
190 
191  std::tuple<int, int> data = m_phaseDurations.at(phase);
192  int min = std::get<0>(data);
193  int max = std::get<1>(data);
194  if(min == max) return min;
195  return (int)stdext::random_range((long)min, (long)max);
196 }
197 
198 void Animator::calculateSynchronous()
199 {
200  int totalDuration = 0;
201  for(int i = 0; i < m_animationPhases; i++)
202  totalDuration += getPhaseDuration(i);
203 
204  ticks_t ticks = g_clock.millis();
205  int elapsedTicks = (int)(ticks % totalDuration);
206  int totalTime = 0;
207  for(int i = 0; i < m_animationPhases; i++) {
208  int duration = getPhaseDuration(i);
209  if(elapsedTicks >= totalTime && elapsedTicks < totalTime + duration) {
210  m_phase = i;
211  m_currentDuration = duration - (elapsedTicks - totalTime);
212  break;
213  }
214  totalTime += duration;
215  }
216  m_lastPhaseTicks = ticks;
217 }
218 
220 {
221  ticks_t time = 0;
222  for (const auto &pair: m_phaseDurations) {
223  time += std::get<1>(pair);
224  }
225 
226  return time;
227 }
AnimDirBackward
@ AnimDirBackward
Definition: animator.h:40
FileStream::addU32
void addU32(uint32 v)
Definition: filestream.cpp:377
Animator::unserialize
void unserialize(int animationPhases, const FileStreamPtr &fin)
Definition: animator.cpp:43
Animator::getTotalDuration
ticks_t getTotalDuration()
Definition: animator.cpp:219
ticks_t
int64 ticks_t
Definition: types.h:43
AnimDirForward
@ AnimDirForward
Definition: animator.h:39
Animator::serialize
void serialize(const FileStreamPtr &fin)
Definition: animator.cpp:62
stdext::time
ticks_t time()
Definition: time.cpp:33
clock.h
Animator::getPhaseAt
int getPhaseAt(ticks_t time)
Definition: animator.cpp:126
declarations.h
AnimPhaseAutomatic
@ AnimPhaseAutomatic
Definition: animator.h:32
FileStream::get8
int8 get8()
Definition: filestream.cpp:246
FileStream::add8
void add8(int8 v)
Definition: filestream.cpp:401
FileStream::add32
void add32(int32 v)
Definition: filestream.cpp:424
filestream.h
FileStream::get32
int32 get32()
Definition: filestream.cpp:278
stdext::shared_object_ptr< FileStream >
FileStream::getU32
uint32 getU32()
Definition: filestream.cpp:215
Clock::millis
ticks_t millis()
Definition: clock.h:37
Animator::Animator
Animator()
Definition: animator.cpp:29
stdext::random_range
long random_range(long min, long max)
Definition: math.cpp:48
FileStream::addU8
void addU8(uint8 v)
Definition: filestream.cpp:354
AnimPhaseAsync
@ AnimPhaseAsync
Definition: animator.h:34
Animator::getStartPhase
int getStartPhase()
Definition: animator.cpp:144
g_clock
Clock g_clock
Definition: clock.cpp:25
FileStream::getU8
uint8 getU8()
Definition: filestream.cpp:183
AnimPhaseRandom
@ AnimPhaseRandom
Definition: animator.h:33
animator.h
Animator::resetAnimation
void resetAnimation()
Definition: animator.cpp:151
Animator::getPhase
int getPhase()
Definition: animator.cpp:96
Animator::setPhase
void setPhase(int phase)
Definition: animator.cpp:74