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