aboutsummaryrefslogtreecommitdiff
path: root/engines/tinsel/anim.cpp
blob: e241c7cba0497834b456ff58288decf968a355b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * This file contains utilities to handle object animation.
 */

#include "tinsel/anim.h"
#include "tinsel/handle.h"
#include "tinsel/multiobj.h"	// multi-part object defintions etc.
#include "tinsel/object.h"
#include "tinsel/sched.h"
#include "tinsel/tinsel.h"

#include "common/textconsole.h"
#include "common/util.h"

namespace Tinsel {

/**
 * Advance to next frame routine.
 * @param pAnim			Animation data structure
 */
SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
	// get a pointer to the script
	const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);

	while (1) {	// repeat until a real image
		debugC(DEBUG_DETAILED, kTinselDebugAnimations,
		"DoNextFrame %ph index=%d, op=%xh", (const void *)pAnim, pAnim->scriptIndex,
		FROM_32(pAni[pAnim->scriptIndex].op));

		switch ((int32)FROM_32(pAni[pAnim->scriptIndex].op)) {
		case ANI_END:	// end of animation script

			// move to next opcode
			pAnim->scriptIndex++;

			// indicate script has finished
			return ScriptFinished;

		case ANI_JUMP:	// do animation jump

			// move to jump address
			pAnim->scriptIndex++;

			// jump to new frame position
			pAnim->scriptIndex += (int32)FROM_32(pAni[pAnim->scriptIndex].op);

			// go fetch a real image
			break;

		case ANI_HFLIP:	// flip animated object horizontally

			// next opcode
			pAnim->scriptIndex++;

			MultiHorizontalFlip(pAnim->pObject);

			// go fetch a real image
			break;

		case ANI_VFLIP:	// flip animated object vertically

			// next opcode
			pAnim->scriptIndex++;

			MultiVerticalFlip(pAnim->pObject);

			// go fetch a real image
			break;
		case ANI_HVFLIP:	// flip animated object in both directions

			// next opcode
			pAnim->scriptIndex++;

			MultiHorizontalFlip(pAnim->pObject);
			MultiVerticalFlip(pAnim->pObject);

			// go fetch a real image
			break;

		case ANI_ADJUSTX:	// adjust animated object x animation point

			// move to x adjustment operand
			pAnim->scriptIndex++;

			MultiAdjustXY(pAnim->pObject, (int32)FROM_32(pAni[pAnim->scriptIndex].op), 0);

			// next opcode
			pAnim->scriptIndex++;

			// go fetch a real image
			break;

		case ANI_ADJUSTY:	// adjust animated object y animation point

			// move to y adjustment operand
			pAnim->scriptIndex++;

			MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_32(pAni[pAnim->scriptIndex].op));

			// next opcode
			pAnim->scriptIndex++;

			// go fetch a real image
			break;

		case ANI_ADJUSTXY:	// adjust animated object x & y animation points
		{
			int x, y;

			// move to x adjustment operand
			pAnim->scriptIndex++;
			x = (int32)FROM_32(pAni[pAnim->scriptIndex].op);

			// move to y adjustment operand
			pAnim->scriptIndex++;
			y = (int32)FROM_32(pAni[pAnim->scriptIndex].op);

			MultiAdjustXY(pAnim->pObject, x, y);

			// next opcode
			pAnim->scriptIndex++;

			// go fetch a real image
			break;
		}

		case ANI_NOSLEEP:	// do not sleep for this frame

			// next opcode
			pAnim->scriptIndex++;

			// indicate not to sleep
			return ScriptNoSleep;

		case ANI_CALL:		// call routine

			// move to function address
			pAnim->scriptIndex++;

			// make function call

			// REMOVED BUGGY CODE
			// pFunc is a function pointer that's part of a union and is assumed to be 32-bits.
			// There is no known place where a function pointer is stored inside the animation
			// scripts, something which wouldn't have worked anyway. Having played through the
			// entire game, there hasn't been any occurence of this case, so just error out here
			// in case we missed something (highly unlikely though)
			error("ANI_CALL opcode encountered! Please report this error to the ScummVM team");
			//(*pAni[pAnim->scriptIndex].pFunc)(pAnim);
			return ScriptSleep;	// for compilers that don't support NORETURN

#if 0
			// next opcode
			pAnim->scriptIndex++;

			// go fetch a real image
			break;
#endif

		case ANI_HIDE:		// hide animated object

			MultiHideObject(pAnim->pObject);

			// next opcode
			pAnim->scriptIndex++;

			// dont skip a sleep
			return ScriptSleep;

		default:	// must be an actual animation frame handle

			// set objects new animation frame
			pAnim->pObject->hShape = FROM_32(pAni[pAnim->scriptIndex].hFrame);

			// re-shape the object
			MultiReshape(pAnim->pObject);

			// next opcode
			pAnim->scriptIndex++;

			// dont skip a sleep
			return ScriptSleep;
		}
	}
}

/**
 * Init a ANIM structure for single stepping through a animation script.
 * @param pAnim				Animation data structure
 * @param pAniObj			Object to animate
 * @param hNewScript		Script of multipart frames
 * @param aniSpeed			Sets speed of animation in frames
 */
void InitStepAnimScript(ANIM *pAnim, OBJECT *pAniObj, SCNHANDLE hNewScript, int aniSpeed) {
	OBJECT *pObj;			// multi-object list iterator

	debugC(DEBUG_DETAILED, kTinselDebugAnimations,
		"InitStepAnimScript Object=(%d,%d,%xh) script=%xh aniSpeed=%d rec=%ph",
		!pAniObj ? 0 : fracToInt(pAniObj->xPos),
		!pAniObj ? 0 : fracToInt(pAniObj->yPos),
		!pAniObj ? 0 : pAniObj->hImg, hNewScript, aniSpeed, (void *)pAnim);

	pAnim->aniDelta = 1;		// will animate on next call to NextAnimRate
	pAnim->pObject = pAniObj;	// set object to animate
	pAnim->hScript = hNewScript;	// set animation script
	pAnim->scriptIndex = 0;		// start of script
	pAnim->aniRate = aniSpeed;	// set speed of animation

	// reset flip flags for the object - let the script do the flipping
	for (pObj = pAniObj; pObj != NULL; pObj = pObj->pSlave) {
		AnimateObjectFlags(pObj, pObj->flags & ~(DMA_FLIPH | DMA_FLIPV),
			pObj->hImg);
	}
}

/**
 * Execute the next command in a animation script.
 * @param pAnim			Animation data structure
 */
SCRIPTSTATE StepAnimScript(ANIM *pAnim) {
	SCRIPTSTATE state;

	if (--pAnim->aniDelta == 0) {
		// re-init animation delta counter
		pAnim->aniDelta = pAnim->aniRate;

		if (TinselV2)
			state = DoNextFrame(pAnim);
		else {
			// move to next frame
			while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
				;
		}

		return state;
	}

	// indicate calling task should sleep
	return ScriptSleep;
}

/**
 * Skip the specified number of frames.
 * @param pAnim			Animation data structure
 * @param numFrames		Number of frames to skip
 */
void SkipFrames(ANIM *pAnim, int numFrames) {
	// get a pointer to the script
	const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);

	if (!TinselV2 && (numFrames <= 0))
		// do nothing
		return;

	while (1) {	// repeat until a real image

		switch ((int32)FROM_32(pAni[pAnim->scriptIndex].op)) {
		case ANI_END:	// end of animation script
			// going off the end is probably a error, but only in Tinsel 1
			if (!TinselV2)
				error("SkipFrames(): formally 'assert(0)!'");
			return;

		case ANI_JUMP:	// do animation jump

			// move to jump address
			pAnim->scriptIndex++;

			// jump to new frame position
			pAnim->scriptIndex += (int32)FROM_32(pAni[pAnim->scriptIndex].op);

			if (TinselV2)
				// Done if skip to jump
				return;
			break;

		case ANI_HFLIP:	// flip animated object horizontally

			// next opcode
			pAnim->scriptIndex++;

			MultiHorizontalFlip(pAnim->pObject);
			break;

		case ANI_VFLIP:	// flip animated object vertically

			// next opcode
			pAnim->scriptIndex++;

			MultiVerticalFlip(pAnim->pObject);
			break;

		case ANI_HVFLIP:	// flip animated object in both directions

			// next opcode
			pAnim->scriptIndex++;

			MultiHorizontalFlip(pAnim->pObject);
			MultiVerticalFlip(pAnim->pObject);
			break;

		case ANI_ADJUSTX:	// adjust animated object x animation point

			// move to x adjustment operand
			pAnim->scriptIndex++;

			MultiAdjustXY(pAnim->pObject, (int32)FROM_32(pAni[pAnim->scriptIndex].op), 0);

			// next opcode
			pAnim->scriptIndex++;
			break;

		case ANI_ADJUSTY:	// adjust animated object y animation point

			// move to y adjustment operand
			pAnim->scriptIndex++;

			MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_32(pAni[pAnim->scriptIndex].op));

			// next opcode
			pAnim->scriptIndex++;
			break;

		case ANI_ADJUSTXY:	// adjust animated object x & y animation points
		{
			int x, y;

			// move to x adjustment operand
			pAnim->scriptIndex++;
			x = (int32)FROM_32(pAni[pAnim->scriptIndex].op);

			// move to y adjustment operand
			pAnim->scriptIndex++;
			y = (int32)FROM_32(pAni[pAnim->scriptIndex].op);

			MultiAdjustXY(pAnim->pObject, x, y);

			// next opcode
			pAnim->scriptIndex++;

			break;
		}

		case ANI_NOSLEEP:	// do not sleep for this frame

			// next opcode
			pAnim->scriptIndex++;
			break;

		case ANI_CALL:		// call routine

			// skip function address
			pAnim->scriptIndex += 2;
			break;

		case ANI_HIDE:		// hide animated object

			// next opcode
			pAnim->scriptIndex++;
			break;

		default:	// must be an actual animation frame handle

			// one less frame
			if (numFrames == 0)
				return;

			if (numFrames == -1 || numFrames-- > 0) {
				// next opcode
				pAnim->scriptIndex++;
			} else {
				// set objects new animation frame
				pAnim->pObject->hShape = FROM_32(pAni[pAnim->scriptIndex].hFrame);

				// re-shape the object
				MultiReshape(pAnim->pObject);

				// we have skipped to the correct place
				return;
			}
			break;
		}
	}
}

/**
 * About to jump or end
 * @param pAnim			Animation data structure
 */
bool AboutToJumpOrEnd(PANIM pAnim) {
	if (pAnim->aniDelta == 1) {
		// get a pointer to the script
		ANI_SCRIPT *pAni = (ANI_SCRIPT *)LockMem(pAnim->hScript);
		int	zzz = pAnim->scriptIndex;

		for (;;) {
			// repeat until a real image
			switch (FROM_32(pAni[zzz].op)) {
			case ANI_END:		// end of animation script
			case ANI_JUMP:		// do animation jump
				return true;

			case ANI_HFLIP:		// flip animated object horizontally
			case ANI_VFLIP:		// flip animated object vertically
			case ANI_HVFLIP:	// flip animated object in both directions
				zzz++;
				break;

			case ANI_ADJUSTX:	// adjust animated object x animation point
			case ANI_ADJUSTY:	// adjust animated object y animation point
				zzz += 2;
				break;

			case ANI_ADJUSTXY:	// adjust animated object x & y animation points
				zzz += 3;
				break;

			case ANI_HIDE:		// hide animated object
			default:		// must be an actual animation frame handle
				return false;
			}
		}
	}

	return false;
}



} // End of namespace Tinsel