1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * This file contains utilities to handle object animation.
22  */
23 
24 #include "tinsel/anim.h"
25 #include "tinsel/handle.h"
26 #include "tinsel/multiobj.h"	// multi-part object defintions etc.
27 #include "tinsel/object.h"
28 #include "tinsel/sched.h"
29 #include "tinsel/tinsel.h"
30 
31 #include "common/textconsole.h"
32 #include "common/util.h"
33 
34 namespace Tinsel {
35 
36 /**
37  * Advance to next frame routine.
38  * @param pAnim			Animation data structure
39  */
DoNextFrame(ANIM * pAnim)40 SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
41 	// get a pointer to the script
42 	const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
43 
44 	while (1) {	// repeat until a real image
45 		debugC(DEBUG_DETAILED, kTinselDebugAnimations,
46 		"DoNextFrame %ph index=%d, op=%xh", (const void *)pAnim, pAnim->scriptIndex,
47 		FROM_32(pAni[pAnim->scriptIndex].op));
48 
49 		switch ((int32)FROM_32(pAni[pAnim->scriptIndex].op)) {
50 		case ANI_END:	// end of animation script
51 
52 			// move to next opcode
53 			pAnim->scriptIndex++;
54 
55 			// indicate script has finished
56 			return ScriptFinished;
57 
58 		case ANI_JUMP:	// do animation jump
59 
60 			// move to jump address
61 			pAnim->scriptIndex++;
62 
63 			// jump to new frame position
64 			pAnim->scriptIndex += (int32)FROM_32(pAni[pAnim->scriptIndex].op);
65 
66 			// go fetch a real image
67 			break;
68 
69 		case ANI_HFLIP:	// flip animated object horizontally
70 
71 			// next opcode
72 			pAnim->scriptIndex++;
73 
74 			MultiHorizontalFlip(pAnim->pObject);
75 
76 			// go fetch a real image
77 			break;
78 
79 		case ANI_VFLIP:	// flip animated object vertically
80 
81 			// next opcode
82 			pAnim->scriptIndex++;
83 
84 			MultiVerticalFlip(pAnim->pObject);
85 
86 			// go fetch a real image
87 			break;
88 		case ANI_HVFLIP:	// flip animated object in both directions
89 
90 			// next opcode
91 			pAnim->scriptIndex++;
92 
93 			MultiHorizontalFlip(pAnim->pObject);
94 			MultiVerticalFlip(pAnim->pObject);
95 
96 			// go fetch a real image
97 			break;
98 
99 		case ANI_ADJUSTX:	// adjust animated object x animation point
100 
101 			// move to x adjustment operand
102 			pAnim->scriptIndex++;
103 
104 			MultiAdjustXY(pAnim->pObject, (int32)FROM_32(pAni[pAnim->scriptIndex].op), 0);
105 
106 			// next opcode
107 			pAnim->scriptIndex++;
108 
109 			// go fetch a real image
110 			break;
111 
112 		case ANI_ADJUSTY:	// adjust animated object y animation point
113 
114 			// move to y adjustment operand
115 			pAnim->scriptIndex++;
116 
117 			MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_32(pAni[pAnim->scriptIndex].op));
118 
119 			// next opcode
120 			pAnim->scriptIndex++;
121 
122 			// go fetch a real image
123 			break;
124 
125 		case ANI_ADJUSTXY:	// adjust animated object x & y animation points
126 		{
127 			int x, y;
128 
129 			// move to x adjustment operand
130 			pAnim->scriptIndex++;
131 			x = (int32)FROM_32(pAni[pAnim->scriptIndex].op);
132 
133 			// move to y adjustment operand
134 			pAnim->scriptIndex++;
135 			y = (int32)FROM_32(pAni[pAnim->scriptIndex].op);
136 
137 			MultiAdjustXY(pAnim->pObject, x, y);
138 
139 			// next opcode
140 			pAnim->scriptIndex++;
141 
142 			// go fetch a real image
143 			break;
144 		}
145 
146 		case ANI_NOSLEEP:	// do not sleep for this frame
147 
148 			// next opcode
149 			pAnim->scriptIndex++;
150 
151 			// indicate not to sleep
152 			return ScriptNoSleep;
153 
154 		case ANI_CALL:		// call routine
155 
156 			// move to function address
157 			pAnim->scriptIndex++;
158 
159 			// make function call
160 
161 			// REMOVED BUGGY CODE
162 			// pFunc is a function pointer that's part of a union and is assumed to be 32-bits.
163 			// There is no known place where a function pointer is stored inside the animation
164 			// scripts, something which wouldn't have worked anyway. Having played through the
165 			// entire game, there hasn't been any occurence of this case, so just error out here
166 			// in case we missed something (highly unlikely though)
167 			error("ANI_CALL opcode encountered! Please report this error to the ScummVM team");
168 			//(*pAni[pAnim->scriptIndex].pFunc)(pAnim);
169 			return ScriptSleep;	// for compilers that don't support NORETURN
170 
171 #if 0
172 			// next opcode
173 			pAnim->scriptIndex++;
174 
175 			// go fetch a real image
176 			break;
177 #endif
178 
179 		case ANI_HIDE:		// hide animated object
180 
181 			MultiHideObject(pAnim->pObject);
182 
183 			// next opcode
184 			pAnim->scriptIndex++;
185 
186 			// dont skip a sleep
187 			return ScriptSleep;
188 
189 		default:	// must be an actual animation frame handle
190 
191 			// set objects new animation frame
192 			pAnim->pObject->hShape = FROM_32(pAni[pAnim->scriptIndex].hFrame);
193 
194 			// re-shape the object
195 			MultiReshape(pAnim->pObject);
196 
197 			// next opcode
198 			pAnim->scriptIndex++;
199 
200 			// dont skip a sleep
201 			return ScriptSleep;
202 		}
203 	}
204 }
205 
206 /**
207  * Init a ANIM structure for single stepping through a animation script.
208  * @param pAnim				Animation data structure
209  * @param pAniObj			Object to animate
210  * @param hNewScript		Script of multipart frames
211  * @param aniSpeed			Sets speed of animation in frames
212  */
InitStepAnimScript(ANIM * pAnim,OBJECT * pAniObj,SCNHANDLE hNewScript,int aniSpeed)213 void InitStepAnimScript(ANIM *pAnim, OBJECT *pAniObj, SCNHANDLE hNewScript, int aniSpeed) {
214 	OBJECT *pObj;			// multi-object list iterator
215 
216 	debugC(DEBUG_DETAILED, kTinselDebugAnimations,
217 		"InitStepAnimScript Object=(%d,%d,%xh) script=%xh aniSpeed=%d rec=%ph",
218 		!pAniObj ? 0 : fracToInt(pAniObj->xPos),
219 		!pAniObj ? 0 : fracToInt(pAniObj->yPos),
220 		!pAniObj ? 0 : pAniObj->hImg, hNewScript, aniSpeed, (void *)pAnim);
221 
222 	pAnim->aniDelta = 1;		// will animate on next call to NextAnimRate
223 	pAnim->pObject = pAniObj;	// set object to animate
224 	pAnim->hScript = hNewScript;	// set animation script
225 	pAnim->scriptIndex = 0;		// start of script
226 	pAnim->aniRate = aniSpeed;	// set speed of animation
227 
228 	// reset flip flags for the object - let the script do the flipping
229 	for (pObj = pAniObj; pObj != NULL; pObj = pObj->pSlave) {
230 		AnimateObjectFlags(pObj, pObj->flags & ~(DMA_FLIPH | DMA_FLIPV),
231 			pObj->hImg);
232 	}
233 }
234 
235 /**
236  * Execute the next command in a animation script.
237  * @param pAnim			Animation data structure
238  */
StepAnimScript(ANIM * pAnim)239 SCRIPTSTATE StepAnimScript(ANIM *pAnim) {
240 	SCRIPTSTATE state;
241 
242 	if (--pAnim->aniDelta == 0) {
243 		// re-init animation delta counter
244 		pAnim->aniDelta = pAnim->aniRate;
245 
246 		if (TinselV2)
247 			state = DoNextFrame(pAnim);
248 		else {
249 			// move to next frame
250 			while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
251 				;
252 		}
253 
254 		return state;
255 	}
256 
257 	// indicate calling task should sleep
258 	return ScriptSleep;
259 }
260 
261 /**
262  * Skip the specified number of frames.
263  * @param pAnim			Animation data structure
264  * @param numFrames		Number of frames to skip
265  */
SkipFrames(ANIM * pAnim,int numFrames)266 void SkipFrames(ANIM *pAnim, int numFrames) {
267 	// get a pointer to the script
268 	const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
269 
270 	if (!TinselV2 && (numFrames <= 0))
271 		// do nothing
272 		return;
273 
274 	while (1) {	// repeat until a real image
275 
276 		switch ((int32)FROM_32(pAni[pAnim->scriptIndex].op)) {
277 		case ANI_END:	// end of animation script
278 			// going off the end is probably a error, but only in Tinsel 1
279 			if (!TinselV2)
280 				error("SkipFrames(): formally 'assert(0)!'");
281 			return;
282 
283 		case ANI_JUMP:	// do animation jump
284 
285 			// move to jump address
286 			pAnim->scriptIndex++;
287 
288 			// jump to new frame position
289 			pAnim->scriptIndex += (int32)FROM_32(pAni[pAnim->scriptIndex].op);
290 
291 			if (TinselV2)
292 				// Done if skip to jump
293 				return;
294 			break;
295 
296 		case ANI_HFLIP:	// flip animated object horizontally
297 
298 			// next opcode
299 			pAnim->scriptIndex++;
300 
301 			MultiHorizontalFlip(pAnim->pObject);
302 			break;
303 
304 		case ANI_VFLIP:	// flip animated object vertically
305 
306 			// next opcode
307 			pAnim->scriptIndex++;
308 
309 			MultiVerticalFlip(pAnim->pObject);
310 			break;
311 
312 		case ANI_HVFLIP:	// flip animated object in both directions
313 
314 			// next opcode
315 			pAnim->scriptIndex++;
316 
317 			MultiHorizontalFlip(pAnim->pObject);
318 			MultiVerticalFlip(pAnim->pObject);
319 			break;
320 
321 		case ANI_ADJUSTX:	// adjust animated object x animation point
322 
323 			// move to x adjustment operand
324 			pAnim->scriptIndex++;
325 
326 			MultiAdjustXY(pAnim->pObject, (int32)FROM_32(pAni[pAnim->scriptIndex].op), 0);
327 
328 			// next opcode
329 			pAnim->scriptIndex++;
330 			break;
331 
332 		case ANI_ADJUSTY:	// adjust animated object y animation point
333 
334 			// move to y adjustment operand
335 			pAnim->scriptIndex++;
336 
337 			MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_32(pAni[pAnim->scriptIndex].op));
338 
339 			// next opcode
340 			pAnim->scriptIndex++;
341 			break;
342 
343 		case ANI_ADJUSTXY:	// adjust animated object x & y animation points
344 		{
345 			int x, y;
346 
347 			// move to x adjustment operand
348 			pAnim->scriptIndex++;
349 			x = (int32)FROM_32(pAni[pAnim->scriptIndex].op);
350 
351 			// move to y adjustment operand
352 			pAnim->scriptIndex++;
353 			y = (int32)FROM_32(pAni[pAnim->scriptIndex].op);
354 
355 			MultiAdjustXY(pAnim->pObject, x, y);
356 
357 			// next opcode
358 			pAnim->scriptIndex++;
359 
360 			break;
361 		}
362 
363 		case ANI_NOSLEEP:	// do not sleep for this frame
364 
365 			// next opcode
366 			pAnim->scriptIndex++;
367 			break;
368 
369 		case ANI_CALL:		// call routine
370 
371 			// skip function address
372 			pAnim->scriptIndex += 2;
373 			break;
374 
375 		case ANI_HIDE:		// hide animated object
376 
377 			// next opcode
378 			pAnim->scriptIndex++;
379 			break;
380 
381 		default:	// must be an actual animation frame handle
382 
383 			// one less frame
384 			if (numFrames == 0)
385 				return;
386 
387 			if (numFrames == -1 || numFrames-- > 0) {
388 				// next opcode
389 				pAnim->scriptIndex++;
390 			} else {
391 				// set objects new animation frame
392 				pAnim->pObject->hShape = FROM_32(pAni[pAnim->scriptIndex].hFrame);
393 
394 				// re-shape the object
395 				MultiReshape(pAnim->pObject);
396 
397 				// we have skipped to the correct place
398 				return;
399 			}
400 			break;
401 		}
402 	}
403 }
404 
405 /**
406  * About to jump or end
407  * @param pAnim			Animation data structure
408  */
AboutToJumpOrEnd(PANIM pAnim)409 bool AboutToJumpOrEnd(PANIM pAnim) {
410 	if (pAnim->aniDelta == 1) {
411 		// get a pointer to the script
412 		ANI_SCRIPT *pAni = (ANI_SCRIPT *)LockMem(pAnim->hScript);
413 		int	zzz = pAnim->scriptIndex;
414 
415 		for (;;) {
416 			// repeat until a real image
417 			switch (FROM_32(pAni[zzz].op)) {
418 			case ANI_END:		// end of animation script
419 			case ANI_JUMP:		// do animation jump
420 				return true;
421 
422 			case ANI_HFLIP:		// flip animated object horizontally
423 			case ANI_VFLIP:		// flip animated object vertically
424 			case ANI_HVFLIP:	// flip animated object in both directions
425 				zzz++;
426 				break;
427 
428 			case ANI_ADJUSTX:	// adjust animated object x animation point
429 			case ANI_ADJUSTY:	// adjust animated object y animation point
430 				zzz += 2;
431 				break;
432 
433 			case ANI_ADJUSTXY:	// adjust animated object x & y animation points
434 				zzz += 3;
435 				break;
436 
437 			case ANI_HIDE:		// hide animated object
438 			default:		// must be an actual animation frame handle
439 				return false;
440 			}
441 		}
442 	}
443 
444 	return false;
445 }
446 
447 
448 
449 } // End of namespace Tinsel
450