animator.cpp

main 241 lines · 6.5 KB Raw
Alex Alex Commit Client Upload Part 2 19/10/2026 00:06
1/*
2 * Copyright (c) 2010-2017 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 "animator.h"
24#include "declarations.h"
25
26#include <framework/core/clock.h>
27#include <framework/core/filestream.h>
28#include <framework/stdext/fastrand.h>
29#include <framework/util/extras.h>
30
31Animator::Animator() {
32 m_animationPhases = 0;
33 m_startPhase = 0;
34 m_loopCount = 0;
35 m_async = false;
36 m_currentDuration = 0;
37 m_currentDirection = AnimDirForward;
38 m_currentLoop = 0;
39 m_lastPhaseTicks = 0;
40 m_isComplete = false;
41 m_phase = 0;
42}
43
44void Animator::unserialize(int animationPhases, const FileStreamPtr &fin) {
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.push_back(
54 std::make_pair(minimum, std::max(0, maximum - minimum)));
55 }
56
57 m_phase = getStartPhase();
58
59 VALIDATE(m_animationPhases == (int)m_phaseDurations.size());
60 VALIDATE(m_startPhase >= -1 && m_startPhase < m_animationPhases);
61}
62
63void Animator::serialize(const FileStreamPtr &fin) {
64 fin->addU8(m_async ? 0 : 1);
65 fin->add32(m_loopCount);
66 fin->add8(m_startPhase);
67
68 for (auto &phase : m_phaseDurations) {
69 fin->addU32(phase.first);
70 fin->addU32(phase.first + phase.second);
71 }
72}
73
74void Animator::setPhase(int phase) {
75 if (m_phase == phase && !m_isComplete)
76 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
96int Animator::getPhase() {
97 ticks_t ticks = g_clock.millis();
98 if (ticks != m_lastPhaseTicks && !m_isComplete) {
99 int elapsedTicks = (int)(ticks - m_lastPhaseTicks);
100 if (elapsedTicks >= m_currentDuration) {
101 int phase = m_phase;
102 while (elapsedTicks >= m_currentDuration) {
103 elapsedTicks -= m_currentDuration;
104 if (m_loopCount < 0)
105 phase = getPingPongPhase();
106 else
107 phase = getLoopPhase();
108
109 if (m_phase != phase) {
110 m_phase = phase;
111 m_currentDuration = getPhaseDuration(phase);
112 } else {
113 m_isComplete = true;
114 break;
115 }
116 }
117 if (!m_isComplete)
118 m_currentDuration -= elapsedTicks;
119 } else {
120 m_currentDuration -= elapsedTicks;
121 }
122 m_lastPhaseTicks = ticks;
123 }
124 return m_phase;
125}
126
127int Animator::getPhaseAt(Timer &timer, int lastPhase) {
128 static int rand_val = 6;
129 ticks_t time = timer.ticksElapsed();
130 for (int i = lastPhase; i < m_animationPhases; ++i) {
131 int phaseDuration = m_phaseDurations[i].second == 0
132 ? m_phaseDurations[i].first
133 : m_phaseDurations[i].first +
134 rand_val % (m_phaseDurations[i].second);
135 rand_val = rand_val * 7 + 11;
136 if (time < phaseDuration) {
137 timer.restart();
138 timer.adjust(-time);
139 return i;
140 }
141 time -= phaseDuration;
142 }
143 return 0;
144 /*
145 ticks_t total = 0;
146
147 for (const auto &pair : m_phaseDurations) {
148 total += std::get<1>(pair);
149
150 if (time < total) {
151 return index;
152 }
153
154 ++index;
155 }
156
157 return std::min<int>(index, m_animationPhases - 1);
158 */
159}
160
161int Animator::getStartPhase() {
162 if (m_startPhase > -1)
163 return m_startPhase;
164 return (int)stdext::random_range(0, (long)m_animationPhases);
165}
166
167void Animator::resetAnimation() {
168 m_isComplete = false;
169 m_currentDirection = AnimDirForward;
170 m_currentLoop = 0;
171 setPhase(AnimPhaseAutomatic);
172}
173
174int Animator::getPingPongPhase() {
175 int count = m_currentDirection == AnimDirForward ? 1 : -1;
176 int nextPhase = m_phase + count;
177 if (nextPhase < 0 || nextPhase >= m_animationPhases) {
178 m_currentDirection =
179 m_currentDirection == AnimDirForward ? AnimDirBackward : AnimDirForward;
180 count *= -1;
181 }
182 return m_phase + count;
183}
184
185int Animator::getLoopPhase() {
186 int nextPhase = m_phase + 1;
187 if (nextPhase < m_animationPhases)
188 return nextPhase;
189
190 if (m_loopCount == 0)
191 return 0;
192
193 if (m_currentLoop < (m_loopCount - 1)) {
194 m_currentLoop++;
195 return 0;
196 }
197
198 return m_phase;
199}
200
201int Animator::getPhaseDuration(int phase) {
202 VALIDATE(phase < (int)m_phaseDurations.size());
203
204 auto &data = m_phaseDurations.at(phase);
205 if (data.second == 0)
206 return data.first;
207 int min = data.first;
208 int max = min + data.second;
209 return (int)stdext::random_range((long)min, (long)max);
210}
211
212void Animator::calculateSynchronous() {
213 m_isComplete = false;
214 int totalDuration = 0;
215 for (int i = 0; i < m_animationPhases; i++)
216 totalDuration += getPhaseDuration(i);
217
218 ticks_t ticks = g_clock.millis();
219 int elapsedTicks = (int)(ticks % totalDuration);
220 int totalTime = 0;
221 for (int i = 0; i < m_animationPhases; i++) {
222 int duration = getPhaseDuration(i);
223 if (elapsedTicks >= totalTime && elapsedTicks < totalTime + duration) {
224 m_phase = i;
225 m_currentDuration = duration - (elapsedTicks - totalTime);
226 break;
227 }
228 totalTime += duration;
229 }
230 m_lastPhaseTicks = ticks;
231}
232
233ticks_t Animator::getTotalDuration() {
234 ticks_t time = 0;
235 for (const auto &pair : m_phaseDurations) {
236 time += pair.first + pair.second;
237 }
238
239 return time;
240}
241
Top