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