renamed and cleaned up source files for handling global animations
[rocksndiamonds.git] / src / anim.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // anim.c
10 // ============================================================================
11
12 #include "anim.h"
13 #include "main.h"
14 #include "tools.h"
15
16
17 /* values for global toon animation definition */
18 #define NUM_GLOBAL_TOON_ANIMS           1
19 #define NUM_GLOBAL_TOON_PARTS           MAX_NUM_TOONS
20
21 /* values for global animation definition (including toons) */
22 #define NUM_GLOBAL_ANIMS_AND_TOONS      (NUM_GLOBAL_ANIMS +             \
23                                          NUM_GLOBAL_TOON_ANIMS)
24 #define NUM_GLOBAL_ANIM_PARTS_AND_TOONS MAX(NUM_GLOBAL_ANIM_PARTS_ALL,  \
25                                             NUM_GLOBAL_TOON_PARTS)
26
27 #define ANIM_CLASS_BIT_TITLE_INITIAL    0
28 #define ANIM_CLASS_BIT_TITLE            1
29 #define ANIM_CLASS_BIT_MAIN             2
30 #define ANIM_CLASS_BIT_SUBMENU          3
31 #define ANIM_CLASS_BIT_MENU             4
32 #define ANIM_CLASS_BIT_TOONS            5
33
34 #define NUM_ANIM_CLASSES                6
35
36 #define ANIM_CLASS_NONE                 0
37 #define ANIM_CLASS_TITLE_INITIAL        (1 << ANIM_CLASS_BIT_TITLE_INITIAL)
38 #define ANIM_CLASS_TITLE                (1 << ANIM_CLASS_BIT_TITLE)
39 #define ANIM_CLASS_MAIN                 (1 << ANIM_CLASS_BIT_MAIN)
40 #define ANIM_CLASS_SUBMENU              (1 << ANIM_CLASS_BIT_SUBMENU)
41 #define ANIM_CLASS_MENU                 (1 << ANIM_CLASS_BIT_MENU)
42 #define ANIM_CLASS_TOONS                (1 << ANIM_CLASS_BIT_TOONS)
43
44 #define ANIM_CLASS_TOONS_MENU_MAIN      (ANIM_CLASS_TOONS |     \
45                                          ANIM_CLASS_MENU  |     \
46                                          ANIM_CLASS_MAIN)
47
48 #define ANIM_CLASS_TOONS_MENU_SUBMENU   (ANIM_CLASS_TOONS |     \
49                                          ANIM_CLASS_MENU  |     \
50                                          ANIM_CLASS_SUBMENU)
51
52 /* values for global animation states */
53 #define ANIM_STATE_INACTIVE             0
54 #define ANIM_STATE_RESTART              (1 << 0)
55 #define ANIM_STATE_WAITING              (1 << 1)
56 #define ANIM_STATE_RUNNING              (1 << 2)
57
58 /* values for global animation control */
59 #define ANIM_START                      0
60 #define ANIM_CONTINUE                   1
61 #define ANIM_STOP                       2
62
63
64 struct GlobalAnimPartControlInfo
65 {
66   int nr;
67   int anim_nr;
68   int mode_nr;
69
70   int sound;
71   int graphic;
72
73   struct GraphicInfo graphic_info;
74   struct GraphicInfo control_info;
75
76   int viewport_x;
77   int viewport_y;
78   int viewport_width;
79   int viewport_height;
80
81   int x, y;
82   int step_xoffset, step_yoffset;
83
84   unsigned int initial_anim_sync_frame;
85   unsigned int step_delay, step_delay_value;
86
87   int init_delay_counter;
88   int anim_delay_counter;
89   int post_delay_counter;
90
91   int drawing_stage;
92
93   int state;
94   int last_anim_status;
95 };
96
97 struct GlobalAnimMainControlInfo
98 {
99   struct GlobalAnimPartControlInfo base;
100   struct GlobalAnimPartControlInfo part[NUM_GLOBAL_ANIM_PARTS_AND_TOONS];
101
102   int nr;
103   int mode_nr;
104
105   struct GraphicInfo control_info;
106
107   int num_parts;
108   int part_counter;
109   int active_part_nr;
110
111   boolean has_base;
112
113   int init_delay_counter;
114
115   int state;
116
117   int last_state, last_active_part_nr;
118 };
119
120 struct GlobalAnimControlInfo
121 {
122   struct GlobalAnimMainControlInfo anim[NUM_GLOBAL_ANIMS_AND_TOONS];
123
124   int nr;
125   int num_anims;
126 };
127
128 struct GameModeAnimClass
129 {
130   int game_mode;
131   int class;
132 } game_mode_anim_classes_list[] =
133 {
134   { GAME_MODE_TITLE_INITIAL_1,          ANIM_CLASS_TITLE_INITIAL        },
135   { GAME_MODE_TITLE_INITIAL_2,          ANIM_CLASS_TITLE_INITIAL        },
136   { GAME_MODE_TITLE_INITIAL_3,          ANIM_CLASS_TITLE_INITIAL        },
137   { GAME_MODE_TITLE_INITIAL_4,          ANIM_CLASS_TITLE_INITIAL        },
138   { GAME_MODE_TITLE_INITIAL_5,          ANIM_CLASS_TITLE_INITIAL        },
139   { GAME_MODE_TITLE_1,                  ANIM_CLASS_TITLE                },
140   { GAME_MODE_TITLE_2,                  ANIM_CLASS_TITLE                },
141   { GAME_MODE_TITLE_3,                  ANIM_CLASS_TITLE                },
142   { GAME_MODE_TITLE_4,                  ANIM_CLASS_TITLE                },
143   { GAME_MODE_TITLE_5,                  ANIM_CLASS_TITLE                },
144   { GAME_MODE_LEVELS,                   ANIM_CLASS_TOONS_MENU_SUBMENU   },
145   { GAME_MODE_LEVELNR,                  ANIM_CLASS_TOONS_MENU_SUBMENU   },
146   { GAME_MODE_INFO,                     ANIM_CLASS_TOONS_MENU_SUBMENU   },
147   { GAME_MODE_SETUP,                    ANIM_CLASS_TOONS_MENU_SUBMENU   },
148   { GAME_MODE_PSEUDO_MAINONLY,          ANIM_CLASS_TOONS_MENU_MAIN      },
149   { GAME_MODE_PSEUDO_TYPENAME,          ANIM_CLASS_TOONS_MENU_MAIN      },
150   { GAME_MODE_SCORES,                   ANIM_CLASS_TOONS                },
151
152   { -1,                                 -1                              }
153 };
154
155 struct AnimClassGameMode
156 {
157   int class_bit;
158   int game_mode;
159 } anim_class_game_modes_list[] =
160 {
161   { ANIM_CLASS_BIT_TITLE_INITIAL,       GAME_MODE_TITLE_INITIAL         },
162   { ANIM_CLASS_BIT_TITLE,               GAME_MODE_TITLE                 },
163   { ANIM_CLASS_BIT_MAIN,                GAME_MODE_MAIN                  },
164   { ANIM_CLASS_BIT_SUBMENU,             GAME_MODE_PSEUDO_SUBMENU        },
165   { ANIM_CLASS_BIT_MENU,                GAME_MODE_PSEUDO_MENU           },
166   { ANIM_CLASS_BIT_TOONS,               GAME_MODE_PSEUDO_TOONS          },
167
168   { -1,                                 -1                              }
169 };
170
171 /* forward declaration for internal use */
172 static void HandleGlobalAnim(int, int);
173 static void DoAnimationExt(void);
174
175 static struct GlobalAnimControlInfo global_anim_ctrl[NUM_GAME_MODES];
176
177 static unsigned int anim_sync_frame = 0;
178
179 static int game_mode_anim_classes[NUM_GAME_MODES];
180 static int anim_class_game_modes[NUM_ANIM_CLASSES];
181
182 static int anim_status_last = GAME_MODE_DEFAULT;
183 static int anim_classes_last = ANIM_CLASS_NONE;
184
185
186 /* ========================================================================= */
187 /* generic animation frame calculation                                       */
188 /* ========================================================================= */
189
190 int getAnimationFrame(int num_frames, int delay, int mode, int start_frame,
191                       int sync_frame)
192 {
193   int frame = 0;
194
195   sync_frame += start_frame * delay;
196
197   if (mode & ANIM_LOOP)                 /* looping animation */
198   {
199     frame = (sync_frame % (delay * num_frames)) / delay;
200   }
201   else if (mode & ANIM_LINEAR)          /* linear (non-looping) animation */
202   {
203     frame = sync_frame / delay;
204
205     if (frame > num_frames - 1)
206       frame = num_frames - 1;
207   }
208   else if (mode & ANIM_PINGPONG)        /* oscillate (border frames once) */
209   {
210     int max_anim_frames = (num_frames > 1 ? 2 * num_frames - 2 : 1);
211
212     frame = (sync_frame % (delay * max_anim_frames)) / delay;
213     frame = (frame < num_frames ? frame : max_anim_frames - frame);
214   }
215   else if (mode & ANIM_PINGPONG2)       /* oscillate (border frames twice) */
216   {
217     int max_anim_frames = 2 * num_frames;
218
219     frame = (sync_frame % (delay * max_anim_frames)) / delay;
220     frame = (frame < num_frames ? frame : max_anim_frames - frame - 1);
221   }
222   else if (mode & ANIM_RANDOM)          /* play frames in random order */
223   {
224     /* note: expect different frames for the same delay cycle! */
225
226     if (gfx.anim_random_frame < 0)
227       frame = GetSimpleRandom(num_frames);
228     else
229       frame = gfx.anim_random_frame % num_frames;
230   }
231   else if (mode & (ANIM_CE_VALUE | ANIM_CE_SCORE | ANIM_CE_DELAY))
232   {
233     frame = sync_frame % num_frames;
234   }
235
236   if (mode & ANIM_REVERSE)              /* use reverse animation direction */
237     frame = num_frames - frame - 1;
238
239   return frame;
240 }
241
242
243 /* ========================================================================= */
244 /* global animation functions                                                */
245 /* ========================================================================= */
246
247 static int getGlobalAnimationPart(struct GlobalAnimMainControlInfo *anim)
248 {
249   struct GraphicInfo *c = &anim->control_info;
250   int last_anim_random_frame = gfx.anim_random_frame;
251   int part_nr;
252
253   gfx.anim_random_frame = -1;   // (use simple, ad-hoc random numbers)
254
255   part_nr = getAnimationFrame(anim->num_parts, 1,
256                               c->anim_mode, c->anim_start_frame,
257                               anim->part_counter);
258
259   gfx.anim_random_frame = last_anim_random_frame;
260
261   return part_nr;
262 }
263
264 static int compareGlobalAnimPartControlInfo(const void *obj1, const void *obj2)
265 {
266   const struct GlobalAnimPartControlInfo *o1 =
267     (struct GlobalAnimPartControlInfo *)obj1;
268   const struct GlobalAnimPartControlInfo *o2 =
269     (struct GlobalAnimPartControlInfo *)obj2;
270   int compare_result;
271
272   if (o1->control_info.draw_order != o2->control_info.draw_order)
273     compare_result = o1->control_info.draw_order - o2->control_info.draw_order;
274   else
275     compare_result = o1->nr - o2->nr;
276
277   return compare_result;
278 }
279
280 static int compareGlobalAnimMainControlInfo(const void *obj1, const void *obj2)
281 {
282   const struct GlobalAnimMainControlInfo *o1 =
283     (struct GlobalAnimMainControlInfo *)obj1;
284   const struct GlobalAnimMainControlInfo *o2 =
285     (struct GlobalAnimMainControlInfo *)obj2;
286   int compare_result;
287
288   if (o1->control_info.draw_order != o2->control_info.draw_order)
289     compare_result = o1->control_info.draw_order - o2->control_info.draw_order;
290   else
291     compare_result = o1->nr - o2->nr;
292
293   return compare_result;
294 }
295
296 static void InitToonControls()
297 {
298   int mode_nr_toons = GAME_MODE_PSEUDO_TOONS;
299   struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr_toons];
300   struct GlobalAnimMainControlInfo *anim = &ctrl->anim[ctrl->num_anims];
301   int mode_nr, anim_nr, part_nr;
302   int control = IMG_INTERNAL_GLOBAL_TOON_DEFAULT;
303   int num_toons = MAX_NUM_TOONS;
304   int i;
305
306   if (global.num_toons >= 0 && global.num_toons < MAX_NUM_TOONS)
307     num_toons = global.num_toons;
308
309   mode_nr = mode_nr_toons;
310   anim_nr = ctrl->num_anims;
311
312   anim->nr = anim_nr;
313   anim->mode_nr = mode_nr;
314   anim->control_info = graphic_info[control];
315
316   anim->num_parts = 0;
317   anim->part_counter = 0;
318   anim->active_part_nr = 0;
319
320   anim->has_base = FALSE;
321
322   anim->init_delay_counter = 0;
323
324   anim->state = ANIM_STATE_INACTIVE;
325
326   part_nr = 0;
327
328   for (i = 0; i < num_toons; i++)
329   {
330     struct GlobalAnimPartControlInfo *part = &anim->part[part_nr];
331     int sound = SND_UNDEFINED;
332     int graphic = IMG_TOON_1 + i;
333     int control = graphic;
334
335     part->nr = part_nr;
336     part->anim_nr = anim_nr;
337     part->mode_nr = mode_nr;
338     part->sound = sound;
339     part->graphic = graphic;
340     part->graphic_info = graphic_info[graphic];
341     part->control_info = graphic_info[control];
342
343     part->graphic_info.anim_delay *= part->graphic_info.step_delay;
344
345     part->control_info.init_delay_fixed = 0;
346     part->control_info.init_delay_random = 150;
347
348     part->control_info.x = ARG_UNDEFINED_VALUE;
349     part->control_info.y = ARG_UNDEFINED_VALUE;
350
351     part->initial_anim_sync_frame = 0;
352
353     part->step_delay = 0;
354     part->step_delay_value = graphic_info[control].step_delay;
355
356     part->state = ANIM_STATE_INACTIVE;
357     part->last_anim_status = -1;
358
359     anim->num_parts++;
360     part_nr++;
361   }
362
363   ctrl->num_anims++;
364 }
365
366 void InitGlobalAnimControls()
367 {
368   int i, m, a, p;
369   int mode_nr, anim_nr, part_nr;
370   int sound, graphic, control;
371
372   anim_sync_frame = 0;
373
374   for (m = 0; m < NUM_GAME_MODES; m++)
375   {
376     mode_nr = m;
377
378     struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr];
379
380     ctrl->nr = mode_nr;
381     ctrl->num_anims = 0;
382
383     anim_nr = 0;
384
385     for (a = 0; a < NUM_GLOBAL_ANIMS; a++)
386     {
387       struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr];
388       int ctrl_id = GLOBAL_ANIM_ID_CONTROL_FIRST + a;
389
390       control = global_anim_info[ctrl_id].graphic[GLOBAL_ANIM_ID_PART_BASE][m];
391
392       // if no base animation parameters defined, use default values
393       if (control == IMG_UNDEFINED)
394         control = IMG_INTERNAL_GLOBAL_ANIM_DEFAULT;
395
396       anim->nr = anim_nr;
397       anim->mode_nr = mode_nr;
398       anim->control_info = graphic_info[control];
399
400       anim->num_parts = 0;
401       anim->part_counter = 0;
402       anim->active_part_nr = 0;
403
404       anim->has_base = FALSE;
405
406       anim->init_delay_counter = 0;
407
408       anim->state = ANIM_STATE_INACTIVE;
409
410       part_nr = 0;
411
412       for (p = 0; p < NUM_GLOBAL_ANIM_PARTS_ALL; p++)
413       {
414         struct GlobalAnimPartControlInfo *part = &anim->part[part_nr];
415
416         sound   = global_anim_info[a].sound[p][m];
417         graphic = global_anim_info[a].graphic[p][m];
418         control = global_anim_info[ctrl_id].graphic[p][m];
419
420         if (graphic == IMG_UNDEFINED || graphic_info[graphic].bitmap == NULL ||
421             control == IMG_UNDEFINED)
422           continue;
423
424 #if 0
425         printf("::: mode == %d, anim = %d, part = %d [%d, %d, %d] [%d]\n",
426                m, a, p, mode_nr, anim_nr, part_nr, control);
427 #endif
428
429 #if 0
430         printf("::: mode == %d, anim = %d, part = %d [%d, %d, %d] [%d]\n",
431                m, a, p, mode_nr, anim_nr, part_nr, sound);
432 #endif
433
434         part->nr = part_nr;
435         part->anim_nr = anim_nr;
436         part->mode_nr = mode_nr;
437         part->sound = sound;
438         part->graphic = graphic;
439         part->graphic_info = graphic_info[graphic];
440         part->control_info = graphic_info[control];
441
442         part->initial_anim_sync_frame = 0;
443
444         part->step_delay = 0;
445         part->step_delay_value = graphic_info[control].step_delay;
446
447         part->state = ANIM_STATE_INACTIVE;
448         part->last_anim_status = -1;
449
450         if (p < GLOBAL_ANIM_ID_PART_BASE)
451         {
452           anim->num_parts++;
453           part_nr++;
454         }
455         else
456         {
457           anim->base = *part;
458           anim->has_base = TRUE;
459         }
460       }
461
462       if (anim->num_parts > 0 || anim->has_base)
463       {
464         ctrl->num_anims++;
465         anim_nr++;
466       }
467     }
468   }
469
470   InitToonControls();
471
472   /* sort all animations according to draw_order and animation number */
473   for (m = 0; m < NUM_GAME_MODES; m++)
474   {
475     struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[m];
476
477     /* sort all main animations for this game mode */
478     qsort(ctrl->anim, ctrl->num_anims,
479           sizeof(struct GlobalAnimMainControlInfo),
480           compareGlobalAnimMainControlInfo);
481
482     for (a = 0; a < ctrl->num_anims; a++)
483     {
484       struct GlobalAnimMainControlInfo *anim = &ctrl->anim[a];
485
486       /* sort all animation parts for this main animation */
487       qsort(anim->part, anim->num_parts,
488             sizeof(struct GlobalAnimPartControlInfo),
489             compareGlobalAnimPartControlInfo);
490     }
491   }
492
493   for (i = 0; i < NUM_GAME_MODES; i++)
494     game_mode_anim_classes[i] = ANIM_CLASS_NONE;
495   for (i = 0; game_mode_anim_classes_list[i].game_mode != -1; i++)
496     game_mode_anim_classes[game_mode_anim_classes_list[i].game_mode] =
497       game_mode_anim_classes_list[i].class;
498
499   for (i = 0; i < NUM_ANIM_CLASSES; i++)
500     anim_class_game_modes[i] = GAME_MODE_DEFAULT;
501   for (i = 0; anim_class_game_modes_list[i].game_mode != -1; i++)
502     anim_class_game_modes[anim_class_game_modes_list[i].class_bit] =
503       anim_class_game_modes_list[i].game_mode;
504
505   anim_status_last = GAME_MODE_LOADING;
506   anim_classes_last = ANIM_CLASS_NONE;
507 }
508
509 void InitGlobalAnimations()
510 {
511   InitGlobalAnimControls();
512 }
513
514 void DrawGlobalAnimationsExt(int drawing_stage)
515 {
516   int mode_nr;
517
518   if (global.anim_status != anim_status_last)
519   {
520     boolean before_fading = (global.anim_status == GAME_MODE_PSEUDO_FADING);
521     boolean after_fading  = (anim_status_last   == GAME_MODE_PSEUDO_FADING);
522     int anim_classes_next = game_mode_anim_classes[global.anim_status_next];
523     int i;
524
525     // ---------- part 1 ------------------------------------------------------
526     // start or stop global animations by change of game mode
527     // (special handling of animations for "current screen" and "all screens")
528
529     // stop animations for last screen
530     HandleGlobalAnim(ANIM_STOP, anim_status_last);
531
532     // start animations for current screen
533     HandleGlobalAnim(ANIM_START, global.anim_status);
534
535     // start animations for all screens after loading new artwork set
536     if (anim_status_last == GAME_MODE_LOADING)
537       HandleGlobalAnim(ANIM_START, GAME_MODE_DEFAULT);
538
539     // ---------- part 2 ------------------------------------------------------
540     // start or stop global animations by change of animation class
541     // (generic handling of animations for "class of screens")
542
543     for (i = 0; i < NUM_ANIM_CLASSES; i++)
544     {
545       int anim_class_check = (1 << i);
546       int anim_class_game_mode = anim_class_game_modes[i];
547       int anim_class_last = anim_classes_last & anim_class_check;
548       int anim_class_next = anim_classes_next & anim_class_check;
549
550       // stop animations for changed screen class before fading to new screen
551       if (before_fading && anim_class_last && !anim_class_next)
552         HandleGlobalAnim(ANIM_STOP, anim_class_game_mode);
553
554       // start animations for changed screen class after fading to new screen
555       if (after_fading && !anim_class_last && anim_class_next)
556         HandleGlobalAnim(ANIM_START, anim_class_game_mode);
557     }
558
559     if (after_fading)
560       anim_classes_last = anim_classes_next;
561
562     anim_status_last = global.anim_status;
563   }
564
565   if (!setup.toons || global.anim_status == GAME_MODE_LOADING)
566     return;
567
568   if (drawing_stage == DRAW_GLOBAL_ANIM_STAGE_1)
569     DoAnimationExt();
570
571   for (mode_nr = 0; mode_nr < NUM_GAME_MODES; mode_nr++)
572   {
573     struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr];
574     int anim_nr;
575
576 #if 0
577     if (mode_nr != GFX_SPECIAL_ARG_DEFAULT &&
578         mode_nr != game_status)
579       continue;
580 #endif
581
582     for (anim_nr = 0; anim_nr < ctrl->num_anims; anim_nr++)
583     {
584       struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr];
585       struct GraphicInfo *c = &anim->control_info;
586       int part_first, part_last;
587       int part_nr;
588
589       if (!(anim->state & ANIM_STATE_RUNNING))
590         continue;
591
592       part_first = part_last = anim->active_part_nr;
593
594       if (c->anim_mode & ANIM_ALL || anim->num_parts == 0)
595       {
596         int num_parts = anim->num_parts + (anim->has_base ? 1 : 0);
597
598         part_first = 0;
599         part_last = num_parts - 1;
600       }
601
602       for (part_nr = part_first; part_nr <= part_last; part_nr++)
603       {
604         struct GlobalAnimPartControlInfo *part = &anim->part[part_nr];
605         struct GraphicInfo *g = &part->graphic_info;
606         Bitmap *src_bitmap;
607         int src_x, src_y;
608         int width  = g->width;
609         int height = g->height;
610         int dst_x = part->x;
611         int dst_y = part->y;
612         int cut_x = 0;
613         int cut_y = 0;
614         int sync_frame;
615         int frame;
616
617         if (!(part->state & ANIM_STATE_RUNNING))
618           continue;
619
620         if (part->drawing_stage != drawing_stage)
621           continue;
622
623         if (part->x < 0)
624         {
625           dst_x = 0;
626           width += part->x;
627           cut_x = -part->x;
628         }
629         else if (part->x > part->viewport_width - g->width)
630           width -= (part->x - (part->viewport_width - g->width));
631
632         if (part->y < 0)
633         {
634           dst_y = 0;
635           height += part->y;
636           cut_y = -part->y;
637         }
638         else if (part->y > part->viewport_height - g->height)
639           height -= (part->y - (part->viewport_height - g->height));
640
641         if (width <= 0 || height <= 0)
642           continue;
643
644         dst_x += part->viewport_x;
645         dst_y += part->viewport_y;
646
647         sync_frame = anim_sync_frame - part->initial_anim_sync_frame;
648         frame = getAnimationFrame(g->anim_frames, g->anim_delay,
649                                   g->anim_mode, g->anim_start_frame,
650                                   sync_frame);
651
652         getFixedGraphicSource(part->graphic, frame, &src_bitmap,
653                               &src_x, &src_y);
654
655         src_x += cut_x;
656         src_y += cut_y;
657
658         BlitToScreenMasked(src_bitmap, src_x, src_y, width, height,
659                            dst_x, dst_y);
660       }
661     }
662   }
663 }
664
665 void DrawGlobalAnimations(int drawing_stage)
666 {
667   DrawGlobalAnimationsExt(drawing_stage);
668 }
669
670 boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part)
671 {
672   int viewport_x;
673   int viewport_y;
674   int viewport_width;
675   int viewport_height;
676   boolean changed = FALSE;
677
678   if (part->last_anim_status == global.anim_status)
679     return FALSE;
680
681   part->last_anim_status = global.anim_status;
682
683   part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_1;
684
685   if (part->control_info.class == get_hash_from_key("window") ||
686       part->control_info.class == get_hash_from_key("border"))
687   {
688     viewport_x = 0;
689     viewport_y = 0;
690     viewport_width  = WIN_XSIZE;
691     viewport_height = WIN_YSIZE;
692
693     part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_2;
694   }
695   else if (part->control_info.class == get_hash_from_key("door_1"))
696   {
697     viewport_x = DX;
698     viewport_y = DY;
699     viewport_width  = DXSIZE;
700     viewport_height = DYSIZE;
701   }
702   else if (part->control_info.class == get_hash_from_key("door_2"))
703   {
704     viewport_x = VX;
705     viewport_y = VY;
706     viewport_width  = VXSIZE;
707     viewport_height = VYSIZE;
708   }
709   else          // default: "playfield"
710   {
711     viewport_x = REAL_SX;
712     viewport_y = REAL_SY;
713     viewport_width  = FULL_SXSIZE;
714     viewport_height = FULL_SYSIZE;
715   }
716
717   if (viewport_x != part->viewport_x ||
718       viewport_y != part->viewport_y ||
719       viewport_width  != part->viewport_width ||
720       viewport_height != part->viewport_height)
721   {
722     part->viewport_x = viewport_x;
723     part->viewport_y = viewport_y;
724     part->viewport_width  = viewport_width;
725     part->viewport_height = viewport_height;
726
727     changed = TRUE;
728   }
729
730   return changed;
731 }
732
733 void PlayGlobalAnimSound(struct GlobalAnimPartControlInfo *part)
734 {
735   int sound = part->sound;
736
737   if (sound == SND_UNDEFINED)
738     return;
739
740   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
741       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
742     return;
743
744   // !!! TODO: ADD STEREO POSITION FOR MOVING ANIMATIONS !!!
745   if (IS_LOOP_SOUND(sound))
746     PlaySoundLoop(sound);
747   else
748     PlaySound(sound);
749
750 #if 0
751   printf("::: PLAY %d.%d.%d: %d\n",
752          part->anim_nr, part->nr, part->mode_nr, sound);
753 #endif
754 }
755
756 void StopGlobalAnimSound(struct GlobalAnimPartControlInfo *part)
757 {
758   int sound = part->sound;
759
760   if (sound == SND_UNDEFINED)
761     return;
762
763   StopSound(sound);
764
765 #if 0
766   printf("::: STOP %d.%d.%d: %d\n",
767          part->anim_nr, part->nr, part->mode_nr, sound);
768 #endif
769 }
770
771 int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part, int state)
772 {
773   struct GraphicInfo *g = &part->graphic_info;
774   struct GraphicInfo *c = &part->control_info;
775   boolean viewport_changed = SetGlobalAnimPart_Viewport(part);
776
777   if (viewport_changed)
778     state |= ANIM_STATE_RESTART;
779
780   if (state & ANIM_STATE_RESTART)
781   {
782     ResetDelayCounterExt(&part->step_delay, anim_sync_frame);
783
784     part->init_delay_counter =
785       (c->init_delay_fixed + GetSimpleRandom(c->init_delay_random));
786
787     part->anim_delay_counter =
788       (c->anim_delay_fixed + GetSimpleRandom(c->anim_delay_random));
789
790     part->initial_anim_sync_frame =
791       (g->anim_global_sync ? 0 : anim_sync_frame + part->init_delay_counter);
792
793     if (c->direction & MV_HORIZONTAL)
794     {
795       int pos_bottom = part->viewport_height - g->height;
796
797       if (c->position == POS_TOP)
798         part->y = 0;
799       else if (c->position == POS_UPPER)
800         part->y = GetSimpleRandom(pos_bottom / 2);
801       else if (c->position == POS_MIDDLE)
802         part->y = pos_bottom / 2;
803       else if (c->position == POS_LOWER)
804         part->y = pos_bottom / 2 + GetSimpleRandom(pos_bottom / 2);
805       else if (c->position == POS_BOTTOM)
806         part->y = pos_bottom;
807       else
808         part->y = GetSimpleRandom(pos_bottom);
809
810       if (c->direction == MV_RIGHT)
811       {
812         part->step_xoffset = c->step_offset;
813         part->x = -g->width + part->step_xoffset;
814       }
815       else
816       {
817         part->step_xoffset = -c->step_offset;
818         part->x = part->viewport_width + part->step_xoffset;
819       }
820
821       part->step_yoffset = 0;
822     }
823     else if (c->direction & MV_VERTICAL)
824     {
825       int pos_right = part->viewport_width - g->width;
826
827       if (c->position == POS_LEFT)
828         part->x = 0;
829       else if (c->position == POS_RIGHT)
830         part->x = pos_right;
831       else
832         part->x = GetSimpleRandom(pos_right);
833
834       if (c->direction == MV_DOWN)
835       {
836         part->step_yoffset = c->step_offset;
837         part->y = -g->height + part->step_yoffset;
838       }
839       else
840       {
841         part->step_yoffset = -c->step_offset;
842         part->y = part->viewport_height + part->step_yoffset;
843       }
844
845       part->step_xoffset = 0;
846     }
847     else
848     {
849       part->x = 0;
850       part->y = 0;
851
852       part->step_xoffset = 0;
853       part->step_yoffset = 0;
854     }
855
856     if (c->x != ARG_UNDEFINED_VALUE)
857       part->x = c->x;
858     if (c->y != ARG_UNDEFINED_VALUE)
859       part->y = c->y;
860
861     if (c->step_xoffset != ARG_UNDEFINED_VALUE)
862       part->step_xoffset = c->step_xoffset;
863     if (c->step_yoffset != ARG_UNDEFINED_VALUE)
864       part->step_yoffset = c->step_yoffset;
865
866     if (part->init_delay_counter == 0)
867       PlayGlobalAnimSound(part);
868   }
869
870   if (part->init_delay_counter > 0)
871   {
872     part->init_delay_counter--;
873
874     if (part->init_delay_counter == 0)
875       PlayGlobalAnimSound(part);
876
877     return ANIM_STATE_WAITING;
878   }
879
880   // check if moving animation has left the visible screen area
881   if ((part->x <= -g->width              && part->step_xoffset <= 0) ||
882       (part->x >=  part->viewport_width  && part->step_xoffset >= 0) ||
883       (part->y <= -g->height             && part->step_yoffset <= 0) ||
884       (part->y >=  part->viewport_height && part->step_yoffset >= 0))
885   {
886     // do not stop animation before "anim" or "post" counter are finished
887     if (part->anim_delay_counter == 0 &&
888         part->post_delay_counter == 0)
889     {
890       StopGlobalAnimSound(part);
891
892       part->post_delay_counter =
893         (c->post_delay_fixed + GetSimpleRandom(c->post_delay_random));
894
895       if (part->post_delay_counter > 0)
896         return ANIM_STATE_RUNNING;
897
898       // drawing last frame not needed here -- animation not visible anymore
899       return ANIM_STATE_RESTART;
900     }
901   }
902
903   if (part->anim_delay_counter > 0)
904   {
905     part->anim_delay_counter--;
906
907     if (part->anim_delay_counter == 0)
908     {
909       StopGlobalAnimSound(part);
910
911       part->post_delay_counter =
912         (c->post_delay_fixed + GetSimpleRandom(c->post_delay_random));
913
914       if (part->post_delay_counter > 0)
915         return ANIM_STATE_RUNNING;
916
917       // additional state "RUNNING" required to not skip drawing last frame
918       return ANIM_STATE_RESTART | ANIM_STATE_RUNNING;
919     }
920   }
921
922   if (part->post_delay_counter > 0)
923   {
924     part->post_delay_counter--;
925
926     if (part->post_delay_counter == 0)
927       return ANIM_STATE_RESTART;
928
929     return ANIM_STATE_WAITING;
930   }
931
932   if (!DelayReachedExt(&part->step_delay, part->step_delay_value,
933                        anim_sync_frame))
934     return ANIM_STATE_RUNNING;
935
936 #if 0
937   {
938     static unsigned int last_counter = -1;
939     unsigned int counter = Counter();
940
941     printf("::: NEXT ANIM PART [%d, %d]\n",
942            anim_sync_frame, counter - last_counter);
943
944     last_counter = counter;
945   }
946 #endif
947
948   part->x += part->step_xoffset;
949   part->y += part->step_yoffset;
950
951   return ANIM_STATE_RUNNING;
952 }
953
954 void HandleGlobalAnim_Main(struct GlobalAnimMainControlInfo *anim, int action)
955 {
956   struct GlobalAnimPartControlInfo *part;
957   struct GraphicInfo *c = &anim->control_info;
958   int state, active_part_nr;
959
960 #if 0
961   printf("::: HandleGlobalAnim_Main: %d, %d => %d\n",
962          anim->mode_nr, anim->nr, anim->num_parts);
963   printf("::: %d, %d, %d\n", global.num_toons, setup.toons, num_toons);
964 #endif
965
966 #if 0
967   printf("::: %s(%d): %d, %d, %d [%d]\n",
968          (action == ANIM_START ? "ANIM_START" :
969           action == ANIM_CONTINUE ? "ANIM_CONTINUE" :
970           action == ANIM_STOP ? "ANIM_STOP" : "(should not happen)"),
971          anim->nr,
972          anim->state & ANIM_STATE_RESTART,
973          anim->state & ANIM_STATE_WAITING,
974          anim->state & ANIM_STATE_RUNNING,
975          anim->num_parts);
976 #endif
977
978   switch (action)
979   {
980     case ANIM_START:
981       anim->state = anim->last_state = ANIM_STATE_RESTART;
982       anim->active_part_nr = anim->last_active_part_nr = 0;
983       anim->part_counter = 0;
984
985       break;
986
987     case ANIM_CONTINUE:
988       if (anim->state == ANIM_STATE_INACTIVE)
989         return;
990
991       anim->state = anim->last_state;
992       anim->active_part_nr = anim->last_active_part_nr;
993
994       break;
995
996     case ANIM_STOP:
997       anim->state = ANIM_STATE_INACTIVE;
998
999       {
1000         int num_parts = anim->num_parts + (anim->has_base ? 1 : 0);
1001         int i;
1002
1003         for (i = 0; i < num_parts; i++)
1004           StopGlobalAnimSound(&anim->part[i]);
1005       }
1006
1007       return;
1008
1009     default:
1010       break;
1011   }
1012
1013   if (c->anim_mode & ANIM_ALL || anim->num_parts == 0)
1014   {
1015     int num_parts = anim->num_parts + (anim->has_base ? 1 : 0);
1016     int i;
1017
1018 #if 0
1019     printf("::: HandleGlobalAnim_Main: %d, %d => %d\n",
1020            anim->mode_nr, anim->nr, num_parts);
1021 #endif
1022
1023     for (i = 0; i < num_parts; i++)
1024     {
1025       part = &anim->part[i];
1026
1027       switch (action)
1028       {
1029         case ANIM_START:
1030           anim->state = ANIM_STATE_RUNNING;
1031           part->state = ANIM_STATE_RESTART;
1032
1033           break;
1034
1035         case ANIM_CONTINUE:
1036           if (part->state == ANIM_STATE_INACTIVE)
1037             continue;
1038
1039           break;
1040
1041         case ANIM_STOP:
1042           part->state = ANIM_STATE_INACTIVE;
1043
1044           continue;
1045
1046         default:
1047           break;
1048       }
1049
1050       part->state = HandleGlobalAnim_Part(part, part->state);
1051
1052       // when animation mode is "once", stop after animation was played once
1053       if (c->anim_mode & ANIM_ONCE &&
1054           part->state & ANIM_STATE_RESTART)
1055         part->state = ANIM_STATE_INACTIVE;
1056     }
1057
1058     anim->last_state = anim->state;
1059     anim->last_active_part_nr = anim->active_part_nr;
1060
1061     return;
1062   }
1063
1064   if (anim->state & ANIM_STATE_RESTART)         // directly after restart
1065     anim->active_part_nr = getGlobalAnimationPart(anim);
1066
1067   part = &anim->part[anim->active_part_nr];
1068
1069   part->state = ANIM_STATE_RUNNING;
1070
1071   anim->state = HandleGlobalAnim_Part(part, anim->state);
1072
1073   if (anim->state & ANIM_STATE_RESTART)
1074     anim->part_counter++;
1075
1076   // when animation mode is "once", stop after all animations were played once
1077   if (c->anim_mode & ANIM_ONCE &&
1078       anim->part_counter == anim->num_parts)
1079     anim->state = ANIM_STATE_INACTIVE;
1080
1081   state = anim->state;
1082   active_part_nr = anim->active_part_nr;
1083
1084   // while the animation parts are pausing (waiting or inactive), play the base
1085   // (main) animation; this corresponds to the "boring player animation" logic
1086   // (!!! KLUDGE WARNING: I THINK THIS IS A MESS THAT SHOULD BE CLEANED UP !!!)
1087   if (anim->has_base)
1088   {
1089     if (anim->state == ANIM_STATE_WAITING ||
1090         anim->state == ANIM_STATE_INACTIVE)
1091     {
1092       anim->active_part_nr = anim->num_parts;   // part nr of base animation
1093       part = &anim->part[anim->active_part_nr];
1094
1095       if (anim->state != anim->last_state)
1096         part->state = ANIM_STATE_RESTART;
1097
1098       anim->state = ANIM_STATE_RUNNING;
1099       part->state = HandleGlobalAnim_Part(part, part->state);
1100     }
1101   }
1102
1103   anim->last_state = state;
1104   anim->last_active_part_nr = active_part_nr;
1105 }
1106
1107 void HandleGlobalAnim_Mode(struct GlobalAnimControlInfo *ctrl, int action)
1108 {
1109   int i;
1110
1111 #if 0
1112   printf("::: HandleGlobalAnim_Mode: %d => %d\n",
1113          ctrl->nr, ctrl->num_anims);
1114 #endif
1115
1116   for (i = 0; i < ctrl->num_anims; i++)
1117     HandleGlobalAnim_Main(&ctrl->anim[i], action);
1118 }
1119
1120 static void HandleGlobalAnim(int action, int game_mode)
1121 {
1122 #if 0
1123   printf("::: HandleGlobalAnim [mode == %d]\n", game_status);
1124 #endif
1125
1126   HandleGlobalAnim_Mode(&global_anim_ctrl[game_mode], action);
1127 }
1128
1129 static void DoAnimationExt()
1130 {
1131   int i;
1132
1133 #if 0
1134   printf("::: DoAnimation [%d, %d]\n", anim_sync_frame, Counter());
1135 #endif
1136
1137   // global animations now synchronized with frame delay of screen update
1138   anim_sync_frame++;
1139
1140   for (i = 0; i < NUM_GAME_MODES; i++)
1141     HandleGlobalAnim(ANIM_CONTINUE, i);
1142
1143 #if 1
1144   // force screen redraw in next frame to continue drawing global animations
1145   redraw_mask = REDRAW_ALL;
1146 #endif
1147 }