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