1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 */
20
21 #include "Animation.h"
22
23 #include "Game.h"
24 #include "Interface.h"
25 #include "Map.h"
26 #include "Sprite2D.h"
27 #include "Video.h"
28 #include "RNG.h"
29
30 namespace GemRB {
31
Animation(int count)32 Animation::Animation(int count)
33 : frames(count, nullptr)
34 {
35 assert(count > 0);
36 indicesCount = count;
37 pos = RAND(0, count-1);
38 starttime = 0;
39 x = 0;
40 y = 0;
41 Flags = A_ANI_ACTIVE;
42 fps = ANI_DEFAULT_FRAMERATE;
43 endReached = false;
44 //behaviour flags
45 playReversed = false;
46 gameAnimation = false;
47 }
48
~Animation(void)49 Animation::~Animation(void)
50 {
51 // Empty, but having it here rather than defined implicitly means
52 // we don't have to include Sprite2D.h in the header.
53 }
54
SetPos(unsigned int index)55 void Animation::SetPos(unsigned int index)
56 {
57 if (index<indicesCount) {
58 pos=index;
59 }
60 starttime = 0;
61 endReached = false;
62 }
63
64 /* when adding NULL, it means we already added a frame of index */
AddFrame(Holder<Sprite2D> frame,unsigned int index)65 void Animation::AddFrame(Holder<Sprite2D> frame, unsigned int index)
66 {
67 if (index>=indicesCount) {
68 error("Animation", "You tried to write past a buffer in animation, BAD!\n");
69 }
70 frames[index] = frame;
71
72 int x = -frame->Frame.x;
73 int y = -frame->Frame.y;
74 int w = frame->Frame.w;
75 int h = frame->Frame.h;
76 if (x < animArea.x) {
77 animArea.w += (animArea.x - x);
78 animArea.x = x;
79 }
80 if (y < animArea.y) {
81 animArea.h += (animArea.y - y);
82 animArea.y = y;
83 }
84 if (x+w > animArea.x+animArea.w) {
85 animArea.w = x+w-animArea.x;
86 }
87 if (y+h > animArea.y+animArea.h) {
88 animArea.h = y+h-animArea.y;
89 }
90 }
91
GetCurrentFrameIndex() const92 unsigned int Animation::GetCurrentFrameIndex() const
93 {
94 if (playReversed)
95 return indicesCount-pos-1;
96 return pos;
97 }
98
CurrentFrame() const99 Holder<Sprite2D> Animation::CurrentFrame() const
100 {
101 return GetFrame(GetCurrentFrameIndex());
102 }
103
LastFrame(void)104 Holder<Sprite2D> Animation::LastFrame(void)
105 {
106 if (!(Flags&A_ANI_ACTIVE)) {
107 Log(MESSAGE, "Sprite2D", "Frame fetched while animation is inactive1!");
108 return NULL;
109 }
110 if (gameAnimation) {
111 starttime = core->GetGame()->Ticks;
112 } else {
113 starttime = GetTicks();
114 }
115 Holder<Sprite2D> ret;
116 if (playReversed)
117 ret = frames[indicesCount-pos-1];
118 else
119 ret = frames[pos];
120 return ret;
121 }
122
NextFrame(void)123 Holder<Sprite2D> Animation::NextFrame(void)
124 {
125 if (!(Flags&A_ANI_ACTIVE)) {
126 Log(MESSAGE, "Sprite2D", "Frame fetched while animation is inactive2!");
127 return NULL;
128 }
129 if (starttime == 0) {
130 if (gameAnimation) {
131 starttime = core->GetGame()->Ticks;
132 } else {
133 starttime = GetTicks();
134 }
135 }
136 Holder<Sprite2D> ret;
137 if (playReversed)
138 ret = frames[indicesCount-pos-1];
139 else
140 ret = frames[pos];
141
142 if (endReached && (Flags&A_ANI_PLAYONCE))
143 return ret;
144
145 unsigned long time;
146 if (gameAnimation) {
147 time = core->GetGame()->Ticks;
148 } else {
149 time = GetTicks();
150 }
151
152 //it could be that we skip more than one frame in case of slow rendering
153 //large, composite animations (dragons, multi-part area anims) require synchronisation
154 if (( time - starttime ) >= ( unsigned long ) ( 1000 / fps )) {
155 int inc = (time-starttime)*fps/1000;
156 pos += inc;
157 starttime += inc*1000/fps;
158 }
159 if (pos >= indicesCount ) {
160 if (indicesCount) {
161 if (Flags&A_ANI_PLAYONCE) {
162 pos = indicesCount-1;
163 endReached = true;
164 } else {
165 pos = pos%indicesCount;
166 endReached = false; //looping, there is no end
167 }
168 } else {
169 pos = 0;
170 endReached = true;
171 }
172 }
173 return ret;
174 }
175
GetSyncedNextFrame(const Animation * master)176 Holder<Sprite2D> Animation::GetSyncedNextFrame(const Animation* master)
177 {
178 if (!(Flags&A_ANI_ACTIVE)) {
179 Log(MESSAGE, "Sprite2D", "Frame fetched while animation is inactive!");
180 return NULL;
181 }
182 Holder<Sprite2D> ret;
183 if (playReversed)
184 ret = frames[indicesCount-pos-1];
185 else
186 ret = frames[pos];
187
188 starttime = master->starttime;
189 endReached = master->endReached;
190
191 //return a valid frame even if the master is longer (e.g. ankhegs)
192 pos = master->pos % indicesCount;
193
194 return ret;
195 }
196
197
release(void)198 void Animation::release(void)
199 {
200 delete this;
201 }
202 /** Gets the i-th frame */
GetFrame(unsigned int i) const203 Holder<Sprite2D> Animation::GetFrame(unsigned int i) const
204 {
205 if (i >= indicesCount) {
206 return NULL;
207 }
208 return frames[i];
209 }
210
MirrorAnimation()211 void Animation::MirrorAnimation()
212 {
213 Video *video = core->GetVideoDriver();
214
215 for (size_t i = 0; i < indicesCount; i++) {
216 frames[i] = video->MirrorSprite(frames[i], BlitFlags::MIRRORX, true);
217 }
218
219 // flip animArea horizontally as well
220 animArea.x = -animArea.w - animArea.x;
221 }
222
MirrorAnimationVert()223 void Animation::MirrorAnimationVert()
224 {
225 Video *video = core->GetVideoDriver();
226
227 for (size_t i = 0; i < indicesCount; i++) {
228 frames[i] = video->MirrorSprite(frames[i], BlitFlags::MIRRORY, true);
229 }
230
231 // flip animArea vertically as well
232 animArea.y = -animArea.h - animArea.y;
233 }
234
AddAnimArea(Animation * slave)235 void Animation::AddAnimArea(Animation* slave)
236 {
237 int x = slave->animArea.x;
238 int y = slave->animArea.y;
239 int w = slave->animArea.w;
240 int h = slave->animArea.h;
241 if (x < animArea.x) {
242 animArea.w += (animArea.x - x);
243 animArea.x = x;
244 }
245 if (y < animArea.y) {
246 animArea.h += (animArea.y - y);
247 animArea.y = y;
248 }
249 if (x+w > animArea.x+animArea.w) {
250 animArea.w = x+w-animArea.x;
251 }
252 if (y+h > animArea.y+animArea.h) {
253 animArea.h = y+h-animArea.y;
254 }
255 }
256
257 }
258