added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / anim.c
index 1e155f9eeacec35859e67fc38087bb8a54f8cb30..1389dc4e8815451aaa2a49d6d78190f841871e22 100644 (file)
@@ -4,7 +4,7 @@
 // (c) 1995-2014 by Artsoft Entertainment
 //                         Holger Schemel
 //                 info@artsoft.org
-//                 http://www.artsoft.org/
+//                 https://www.artsoft.org/
 // ----------------------------------------------------------------------------
 // anim.c
 // ============================================================================
@@ -17,6 +17,7 @@
 #include "files.h"
 #include "events.h"
 #include "screens.h"
+#include "tape.h"
 
 
 #define DEBUG_ANIM_DELAY               0
 #define NUM_GLOBAL_ANIM_PARTS_AND_TOONS        MAX(NUM_GLOBAL_ANIM_PARTS_ALL,  \
                                            NUM_GLOBAL_TOON_PARTS)
 
+#define MAX_GLOBAL_ANIM_LIST           (NUM_GAME_MODES *               \
+                                        NUM_GLOBAL_ANIMS_AND_TOONS *   \
+                                        NUM_GLOBAL_ANIM_PARTS_AND_TOONS)
+
 #define ANIM_CLASS_BIT_TITLE_INITIAL   0
 #define ANIM_CLASS_BIT_TITLE           1
 #define ANIM_CLASS_BIT_MAIN            2
-#define ANIM_CLASS_BIT_SCORES          3
-#define ANIM_CLASS_BIT_SUBMENU         4
-#define ANIM_CLASS_BIT_MENU            5
-#define ANIM_CLASS_BIT_TOONS           6
-#define ANIM_CLASS_BIT_NO_TITLE                7
+#define ANIM_CLASS_BIT_NAMES           3
+#define ANIM_CLASS_BIT_SCORES          4
+#define ANIM_CLASS_BIT_SCORESONLY      5
+#define ANIM_CLASS_BIT_SUBMENU         6
+#define ANIM_CLASS_BIT_MENU            7
+#define ANIM_CLASS_BIT_TOONS           8
+#define ANIM_CLASS_BIT_NO_TITLE                9
 
-#define NUM_ANIM_CLASSES               8
+#define NUM_ANIM_CLASSES               10
 
 #define ANIM_CLASS_NONE                        0
 #define ANIM_CLASS_TITLE_INITIAL       (1 << ANIM_CLASS_BIT_TITLE_INITIAL)
 #define ANIM_CLASS_TITLE               (1 << ANIM_CLASS_BIT_TITLE)
 #define ANIM_CLASS_MAIN                        (1 << ANIM_CLASS_BIT_MAIN)
+#define ANIM_CLASS_NAMES               (1 << ANIM_CLASS_BIT_NAMES)
 #define ANIM_CLASS_SCORES              (1 << ANIM_CLASS_BIT_SCORES)
+#define ANIM_CLASS_SCORESONLY          (1 << ANIM_CLASS_BIT_SCORESONLY)
 #define ANIM_CLASS_SUBMENU             (1 << ANIM_CLASS_BIT_SUBMENU)
 #define ANIM_CLASS_MENU                        (1 << ANIM_CLASS_BIT_MENU)
 #define ANIM_CLASS_TOONS               (1 << ANIM_CLASS_BIT_TOONS)
                                         ANIM_CLASS_SCORES      |       \
                                         ANIM_CLASS_NO_TITLE)
 
+#define ANIM_CLASS_TOONS_SCORESONLY    (ANIM_CLASS_TOONS       |       \
+                                        ANIM_CLASS_SCORES      |       \
+                                        ANIM_CLASS_SCORESONLY  |       \
+                                        ANIM_CLASS_NO_TITLE)
+
 #define ANIM_CLASS_TOONS_MENU_MAIN     (ANIM_CLASS_TOONS       |       \
                                         ANIM_CLASS_MENU        |       \
                                         ANIM_CLASS_MAIN        |       \
                                         ANIM_CLASS_SUBMENU     |       \
                                         ANIM_CLASS_NO_TITLE)
 
+#define ANIM_CLASS_TOONS_MENU_SUBMENU_2        (ANIM_CLASS_TOONS       |       \
+                                        ANIM_CLASS_MENU        |       \
+                                        ANIM_CLASS_SUBMENU     |       \
+                                        ANIM_CLASS_NAMES       |       \
+                                        ANIM_CLASS_NO_TITLE)
+
 // values for global animation states
 #define ANIM_STATE_INACTIVE            0
 #define ANIM_STATE_RESTART             (1 << 0)
@@ -99,6 +119,8 @@ struct GlobalAnimPartControlInfo
   struct GraphicInfo graphic_info;
   struct GraphicInfo control_info;
 
+  boolean class_playfield_or_door;
+
   int viewport_x;
   int viewport_y;
   int viewport_width;
@@ -107,13 +129,21 @@ struct GlobalAnimPartControlInfo
   int x, y;
   int step_xoffset, step_yoffset;
 
+  int tile_x, tile_y;
+  int tile_xoffset, tile_yoffset;
+
   unsigned int initial_anim_sync_frame;
-  unsigned int step_delay, step_delay_value;
+  unsigned int anim_random_frame;
+
+  DelayCounter step_delay;
 
   int init_delay_counter;
   int anim_delay_counter;
   int post_delay_counter;
 
+  int fade_delay_counter;
+  int fade_alpha;
+
   boolean init_event_state;
   boolean anim_event_state;
 
@@ -177,14 +207,18 @@ struct GameModeAnimClass
   { GAME_MODE_TITLE_3,                 ANIM_CLASS_TITLE                },
   { GAME_MODE_TITLE_4,                 ANIM_CLASS_TITLE                },
   { GAME_MODE_TITLE_5,                 ANIM_CLASS_TITLE                },
+  { GAME_MODE_NAMES,                   ANIM_CLASS_TOONS_MENU_SUBMENU   },
   { GAME_MODE_LEVELS,                  ANIM_CLASS_TOONS_MENU_SUBMENU   },
   { GAME_MODE_LEVELNR,                 ANIM_CLASS_TOONS_MENU_SUBMENU   },
   { GAME_MODE_INFO,                    ANIM_CLASS_TOONS_MENU_SUBMENU   },
   { GAME_MODE_SETUP,                   ANIM_CLASS_TOONS_MENU_SUBMENU   },
+  { GAME_MODE_PSEUDO_NAMESONLY,                ANIM_CLASS_TOONS_MENU_SUBMENU_2 },
+  { GAME_MODE_PSEUDO_TYPENAMES,                ANIM_CLASS_TOONS_MENU_SUBMENU_2 },
   { GAME_MODE_PSEUDO_MAINONLY,         ANIM_CLASS_TOONS_MENU_MAIN      },
   { GAME_MODE_PSEUDO_TYPENAME,         ANIM_CLASS_TOONS_MENU_MAIN      },
-  { GAME_MODE_PSEUDO_SCORESOLD,                ANIM_CLASS_TOONS_SCORES         },
-  { GAME_MODE_PSEUDO_SCORESNEW,                ANIM_CLASS_TOONS_SCORES         },
+  { GAME_MODE_PSEUDO_SCORESOLD,                ANIM_CLASS_TOONS_SCORESONLY     },
+  { GAME_MODE_PSEUDO_SCORESNEW,                ANIM_CLASS_TOONS_SCORESONLY     },
+  { GAME_MODE_SCOREINFO,               ANIM_CLASS_TOONS_SCORES         },
   { GAME_MODE_EDITOR,                  ANIM_CLASS_NO_TITLE             },
   { GAME_MODE_PLAYING,                 ANIM_CLASS_NO_TITLE             },
 
@@ -200,7 +234,9 @@ struct AnimClassGameMode
   { ANIM_CLASS_BIT_TITLE_INITIAL,      GAME_MODE_TITLE_INITIAL         },
   { ANIM_CLASS_BIT_TITLE,              GAME_MODE_TITLE                 },
   { ANIM_CLASS_BIT_MAIN,               GAME_MODE_MAIN                  },
+  { ANIM_CLASS_BIT_NAMES,              GAME_MODE_NAMES                 },
   { ANIM_CLASS_BIT_SCORES,             GAME_MODE_SCORES                },
+  { ANIM_CLASS_BIT_SCORESONLY,         GAME_MODE_PSEUDO_SCORESONLY     },
   { ANIM_CLASS_BIT_SUBMENU,            GAME_MODE_PSEUDO_SUBMENU        },
   { ANIM_CLASS_BIT_MENU,               GAME_MODE_PSEUDO_MENU           },
   { ANIM_CLASS_BIT_TOONS,              GAME_MODE_PSEUDO_TOONS          },
@@ -218,6 +254,8 @@ static void ResetGlobalAnim_Clickable(void);
 static void ResetGlobalAnim_Clicked(void);
 
 static struct GlobalAnimControlInfo global_anim_ctrl[NUM_GAME_MODES];
+static struct GlobalAnimPartControlInfo *global_anim_list[MAX_GLOBAL_ANIM_LIST];
+static int num_global_anim_list = 0;
 
 static unsigned int anim_sync_frame = 0;
 
@@ -242,6 +280,9 @@ int getAnimationFrame(int num_frames, int delay, int mode, int start_frame,
 {
   int frame = 0;
 
+  if (delay < 1)                       // delay must be at least 1
+    delay = 1;
+
   sync_frame += start_frame * delay;
 
   if (mode & ANIM_LOOP)                        // looping animation
@@ -278,6 +319,12 @@ int getAnimationFrame(int num_frames, int delay, int mode, int start_frame,
     else
       frame = gfx.anim_random_frame % num_frames;
   }
+  else if (mode & ANIM_LEVEL_NR)       // play frames by level number
+  {
+    int level_pos = level_nr - gfx.anim_first_level;
+
+    frame = level_pos % num_frames;
+  }
   else if (mode & (ANIM_CE_VALUE | ANIM_CE_SCORE | ANIM_CE_DELAY))
   {
     frame = sync_frame % num_frames;
@@ -314,33 +361,42 @@ static int getGlobalAnimationPart(struct GlobalAnimMainControlInfo *anim)
 static int compareGlobalAnimPartControlInfo(const void *obj1, const void *obj2)
 {
   const struct GlobalAnimPartControlInfo *o1 =
-    (struct GlobalAnimPartControlInfo *)obj1;
+    *(struct GlobalAnimPartControlInfo **)obj1;
   const struct GlobalAnimPartControlInfo *o2 =
-    (struct GlobalAnimPartControlInfo *)obj2;
+    *(struct GlobalAnimPartControlInfo **)obj2;
   int compare_result;
 
   if (o1->control_info.draw_order != o2->control_info.draw_order)
     compare_result = o1->control_info.draw_order - o2->control_info.draw_order;
+  else if (o1->mode_nr != o2->mode_nr)
+    compare_result = o1->mode_nr - o2->mode_nr;
+  else if (o1->anim_nr != o2->anim_nr)
+    compare_result = o1->anim_nr - o2->anim_nr;
   else
     compare_result = o1->nr - o2->nr;
 
   return compare_result;
 }
 
-static int compareGlobalAnimMainControlInfo(const void *obj1, const void *obj2)
+static boolean isPausedOnPlayfieldOrDoor(struct GlobalAnimPartControlInfo *part)
 {
-  const struct GlobalAnimMainControlInfo *o1 =
-    (struct GlobalAnimMainControlInfo *)obj1;
-  const struct GlobalAnimMainControlInfo *o2 =
-    (struct GlobalAnimMainControlInfo *)obj2;
-  int compare_result;
+  // only pause playfield and door animations when playing
+  if (game_status != GAME_MODE_PLAYING)
+    return FALSE;
 
-  if (o1->control_info.draw_order != o2->control_info.draw_order)
-    compare_result = o1->control_info.draw_order - o2->control_info.draw_order;
-  else
-    compare_result = o1->nr - o2->nr;
+  // do not pause animations when game ended (and engine is running)
+  if (checkGameEnded())
+    return FALSE;
 
-  return compare_result;
+  // only pause animations on playfield and doors
+  if (!part->class_playfield_or_door)
+    return FALSE;
+
+  // only pause animations when engine is paused or request dialog is active
+  if (!tape.pausing && !game.request_active)
+    return FALSE;
+
+  return TRUE;
 }
 
 static void InitToonControls(void)
@@ -385,7 +441,8 @@ static void InitToonControls(void)
     int sound = SND_UNDEFINED;
     int music = MUS_UNDEFINED;
     int graphic = IMG_TOON_1 + i;
-    int control = graphic;
+
+    control = graphic;
 
     part->nr = part_nr;
     part->anim_nr = anim_nr;
@@ -409,9 +466,10 @@ static void InitToonControls(void)
     part->control_info.y = ARG_UNDEFINED_VALUE;
 
     part->initial_anim_sync_frame = 0;
+    part->anim_random_frame = -1;
 
-    part->step_delay = 0;
-    part->step_delay_value = graphic_info[control].step_delay;
+    part->step_delay.count = 0;
+    part->step_delay.value = graphic_info[control].step_delay;
 
     part->state = ANIM_STATE_INACTIVE;
     part->last_anim_status = -1;
@@ -489,13 +547,15 @@ static void InitGlobalAnimControls(void)
          continue;
 
 #if 0
-       printf("::: mode == %d, anim = %d, part = %d [%d, %d, %d] [%d]\n",
-              m, a, p, mode_nr, anim_nr, part_nr, control);
+       Debug("anim:InitGlobalAnimControls",
+             "mode == %d, anim = %d, part = %d [%d, %d, %d] [%d]",
+             m, a, p, mode_nr, anim_nr, part_nr, control);
 #endif
 
 #if 0
-       printf("::: mode == %d, anim = %d, part = %d [%d, %d, %d] [%d]\n",
-              m, a, p, mode_nr, anim_nr, part_nr, sound);
+       Debug("anim:InitGlobalAnimControls",
+             "mode == %d, anim = %d, part = %d [%d, %d, %d] [%d]",
+             m, a, p, mode_nr, anim_nr, part_nr, sound);
 #endif
 
        part->old_nr = p;
@@ -513,9 +573,10 @@ static void InitGlobalAnimControls(void)
        part->control_info = graphic_info[control];
 
        part->initial_anim_sync_frame = 0;
+       part->anim_random_frame = -1;
 
-       part->step_delay = 0;
-       part->step_delay_value = graphic_info[control].step_delay;
+       part->step_delay.count = 0;
+       part->step_delay.value = graphic_info[control].step_delay;
 
        part->state = ANIM_STATE_INACTIVE;
        part->last_anim_status = -1;
@@ -537,18 +598,14 @@ static void InitGlobalAnimControls(void)
          anim->has_base = TRUE;
        }
 
-       // apply special settings for pointer-style animations
+       // apply special settings to pointer-style animations
        if (part->control_info.class == get_hash_from_key("pointer"))
        {
-         // force animation to be on top (must set anim and part control)
-         if (anim->control_info.draw_order == 0)
-           anim->control_info.draw_order = 1000000;
-         if (part->control_info.draw_order == 0)
-           part->control_info.draw_order = 1000000;
-
-         // force animation to pass-through clicks (must set part control)
-         if (part->control_info.style == STYLE_DEFAULT)
-           part->control_info.style |= STYLE_PASSTHROUGH;
+         // force pointer-style animations to be checked for clicks first
+         part->control_info.draw_order = 1000000;
+
+         // force pointer-style animations to pass-through clicks
+         part->control_info.style |= STYLE_PASSTHROUGH;
        }
       }
 
@@ -562,26 +619,18 @@ static void InitGlobalAnimControls(void)
 
   InitToonControls();
 
-  // sort all animations according to draw_order and animation number
+  // create list of all animation parts
+  num_global_anim_list = 0;
   for (m = 0; m < NUM_GAME_MODES; m++)
-  {
-    struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[m];
+    for (a = 0; a < global_anim_ctrl[m].num_anims; a++)
+      for (p = 0; p < global_anim_ctrl[m].anim[a].num_parts_all; p++)
+       global_anim_list[num_global_anim_list++] =
+         &global_anim_ctrl[m].anim[a].part[p];
 
-    // sort all main animations for this game mode
-    qsort(ctrl->anim, ctrl->num_anims,
-         sizeof(struct GlobalAnimMainControlInfo),
-         compareGlobalAnimMainControlInfo);
-
-    for (a = 0; a < ctrl->num_anims; a++)
-    {
-      struct GlobalAnimMainControlInfo *anim = &ctrl->anim[a];
-
-      // sort all animation parts for this main animation
-      qsort(anim->part, anim->num_parts,
-           sizeof(struct GlobalAnimPartControlInfo),
-           compareGlobalAnimPartControlInfo);
-    }
-  }
+  // sort list of all animation parts according to draw_order and number
+  qsort(global_anim_list, num_global_anim_list,
+       sizeof(struct GlobalAnimPartControlInfo *),
+       compareGlobalAnimPartControlInfo);
 
   for (i = 0; i < NUM_GAME_MODES; i++)
     game_mode_anim_classes[i] = ANIM_CLASS_NONE;
@@ -600,20 +649,143 @@ static void InitGlobalAnimControls(void)
   anim_classes_last = ANIM_CLASS_NONE;
 }
 
+static void SetGlobalAnimEventsForCustomElements(int list_pos)
+{
+  int num_events = GetGlobalAnimEventValueCount(list_pos);
+  int i;
+
+  for (i = 0; i < num_events; i++)
+  {
+    int event = GetGlobalAnimEventValue(list_pos, i);
+
+    if (event & ANIM_EVENT_CE_CHANGE)
+    {
+      int nr = (event >> ANIM_EVENT_CE_BIT) & 0xff;
+
+      if (nr >= 0 && nr < NUM_CUSTOM_ELEMENTS)
+       element_info[EL_CUSTOM_START + nr].has_anim_event = TRUE;
+    }
+  }
+}
+
+void InitGlobalAnimEventsForCustomElements(void)
+{
+  int m, a, p;
+  int control;
+
+  // custom element events for global animations only relevant while playing
+  m = GAME_MODE_PLAYING;
+
+  for (a = 0; a < NUM_GLOBAL_ANIMS; a++)
+  {
+    int ctrl_id = GLOBAL_ANIM_ID_CONTROL_FIRST + a;
+
+    control = global_anim_info[ctrl_id].graphic[GLOBAL_ANIM_ID_PART_BASE][m];
+
+    // if no base animation parameters defined, use default values
+    if (control == IMG_UNDEFINED)
+      control = IMG_INTERNAL_GLOBAL_ANIM_DEFAULT;
+
+    SetGlobalAnimEventsForCustomElements(graphic_info[control].init_event);
+    SetGlobalAnimEventsForCustomElements(graphic_info[control].anim_event);
+
+    for (p = 0; p < NUM_GLOBAL_ANIM_PARTS_ALL; p++)
+    {
+      control = global_anim_info[ctrl_id].graphic[p][m];
+
+      if (control == IMG_UNDEFINED)
+       continue;
+
+      SetGlobalAnimEventsForCustomElements(graphic_info[control].init_event);
+      SetGlobalAnimEventsForCustomElements(graphic_info[control].anim_event);
+    }
+  }
+}
+
 void InitGlobalAnimations(void)
 {
   InitGlobalAnimControls();
 }
 
-static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
+static void BlitGlobalAnimation(struct GlobalAnimPartControlInfo *part,
+                               Bitmap *src_bitmap, int src_x0, int src_y0,
+                               int drawing_target)
 {
+  struct GraphicInfo *g = &part->graphic_info;
+  struct GraphicInfo *c = &part->control_info;
+  void (*blit_bitmap)(Bitmap *, Bitmap *, int, int, int, int, int, int) =
+    (g->draw_masked ? BlitBitmapMasked : BlitBitmap);
+  void (*blit_screen)(Bitmap *, int, int, int, int, int, int) =
+    (g->draw_masked ? BlitToScreenMasked : BlitToScreen);
   Bitmap *fade_bitmap =
     (drawing_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
      drawing_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
+  int alpha = (c->fade_mode & FADE_MODE_FADE ? part->fade_alpha : g->alpha);
+  int x, y;
+
+  for (y = 0; y < c->stacked_yfactor; y++)
+  {
+    for (x = 0; x < c->stacked_xfactor; x++)
+    {
+      int src_x = src_x0;
+      int src_y = src_y0;
+      int dst_x = part->x + x * (g->width  + c->stacked_xoffset);
+      int dst_y = part->y + y * (g->height + c->stacked_yoffset);
+      int cut_x = 0;
+      int cut_y = 0;
+      int width  = g->width;
+      int height = g->height;
+
+      if (dst_x < 0)
+      {
+       width += dst_x;
+       cut_x = -dst_x;
+       dst_x = 0;
+      }
+      else if (dst_x > part->viewport_width - g->width)
+      {
+       width -= (dst_x - (part->viewport_width - g->width));
+      }
+
+      if (dst_y < 0)
+      {
+       height += dst_y;
+       cut_y  = -dst_y;
+       dst_y = 0;
+      }
+      else if (dst_y > part->viewport_height - g->height)
+      {
+       height -= (dst_y - (part->viewport_height - g->height));
+      }
+
+      if (width <= 0 || height <= 0)
+       continue;
+
+      src_x += cut_x;
+      src_y += cut_y;
+
+      dst_x += part->viewport_x;
+      dst_y += part->viewport_y;
+
+      SetBitmapAlphaNextBlit(src_bitmap, alpha);
+
+      if (drawing_target == DRAW_TO_SCREEN)
+       blit_screen(src_bitmap, src_x, src_y, width, height,
+                   dst_x, dst_y);
+      else
+       blit_bitmap(src_bitmap, fade_bitmap, src_x, src_y, width, height,
+                   dst_x, dst_y);
+    }
+  }
+}
+
+static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
+{
   int game_mode_anim_action[NUM_GAME_MODES];
   int mode_nr;
+  int i;
 
-  if (!setup.toons)
+  if (!setup.global_animations)
     return;
 
   if (drawing_stage == DRAW_GLOBAL_ANIM_STAGE_1 &&
@@ -634,9 +806,11 @@ static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
     if (drawing_target == DRAW_TO_FADE_TARGET)
       after_fading = TRUE;
 
-    // special case: changing from/to this screen is done without fading
-    if (global.anim_status == GAME_MODE_PSEUDO_TYPENAME ||
-       anim_status_last   == GAME_MODE_PSEUDO_TYPENAME)
+    // special case: changing from/to these screens is done without fading
+    if (global.anim_status == GAME_MODE_PSEUDO_TYPENAME  ||
+       global.anim_status == GAME_MODE_PSEUDO_TYPENAMES ||
+       anim_status_last   == GAME_MODE_PSEUDO_TYPENAME  ||
+       anim_status_last   == GAME_MODE_PSEUDO_TYPENAMES)
       after_fading = TRUE;
 
     // ---------- part 1 ------------------------------------------------------
@@ -702,118 +876,73 @@ static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
     }
   }
 
+  // when restarting global animations, do not redraw them, but stop here
+  if (drawing_stage == DRAW_GLOBAL_ANIM_STAGE_RESTART)
+    return;
+
   if (global.anim_status == GAME_MODE_LOADING)
     return;
 
-  for (mode_nr = 0; mode_nr < NUM_GAME_MODES; mode_nr++)
+  for (i = 0; i < num_global_anim_list; i++)
   {
-    struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr];
-    int anim_nr;
+    struct GlobalAnimPartControlInfo *part = global_anim_list[i];
+    struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[part->mode_nr];
+    struct GlobalAnimMainControlInfo *anim = &ctrl->anim[part->anim_nr];
+    struct GraphicInfo *g = &part->graphic_info;
+    Bitmap *src_bitmap;
+    int src_x, src_y;
+    int sync_frame;
+    int frame;
+    int last_anim_random_frame = gfx.anim_random_frame;
+
+    if (!setup.toons &&
+       part->graphic >= IMG_TOON_1 &&
+       part->graphic <= IMG_TOON_20)
+      continue;
 
     // when preparing source fading buffer, only draw animations to be stopped
     if (drawing_target == DRAW_TO_FADE_SOURCE &&
-       game_mode_anim_action[mode_nr] != ANIM_STOP)
+       game_mode_anim_action[part->mode_nr] != ANIM_STOP)
       continue;
 
     // when preparing target fading buffer, only draw animations to be started
     if (drawing_target == DRAW_TO_FADE_TARGET &&
-       game_mode_anim_action[mode_nr] != ANIM_START)
+       game_mode_anim_action[part->mode_nr] != ANIM_START)
       continue;
 
-#if 0
-    if (mode_nr != GFX_SPECIAL_ARG_DEFAULT &&
-       mode_nr != game_status)
+    if (!(anim->state & ANIM_STATE_RUNNING))
       continue;
-#endif
-
-    for (anim_nr = 0; anim_nr < ctrl->num_anims; anim_nr++)
-    {
-      struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr];
-      struct GraphicInfo *c = &anim->control_info;
-      int part_first, part_last;
-      int part_nr;
 
-      if (!(anim->state & ANIM_STATE_RUNNING))
-       continue;
-
-      part_first = part_last = anim->active_part_nr;
-
-      if (c->anim_mode & ANIM_ALL || anim->num_parts == 0)
-      {
-       int num_parts = anim->num_parts + (anim->has_base ? 1 : 0);
-
-       part_first = 0;
-       part_last = num_parts - 1;
-      }
-
-      for (part_nr = part_first; part_nr <= part_last; part_nr++)
-      {
-       struct GlobalAnimPartControlInfo *part = &anim->part[part_nr];
-       struct GraphicInfo *g = &part->graphic_info;
-       Bitmap *src_bitmap;
-       int src_x, src_y;
-       int width  = g->width;
-       int height = g->height;
-       int dst_x = part->x;
-       int dst_y = part->y;
-       int cut_x = 0;
-       int cut_y = 0;
-       int sync_frame;
-       int frame;
-       void (*blit_bitmap)(Bitmap *, Bitmap *, int, int, int, int, int, int) =
-         (g->draw_masked ? BlitBitmapMasked : BlitBitmap);
-       void (*blit_screen)(Bitmap *, int, int, int, int, int, int) =
-         (g->draw_masked ? BlitToScreenMasked : BlitToScreen);
-
-       if (!(part->state & ANIM_STATE_RUNNING))
-         continue;
+    if (!(part->state & ANIM_STATE_RUNNING))
+      continue;
 
-       if (part->drawing_stage != drawing_stage)
-         continue;
+    if (part->drawing_stage != drawing_stage)
+      continue;
 
-       if (part->x < 0)
-       {
-         dst_x = 0;
-         width += part->x;
-         cut_x = -part->x;
-       }
-       else if (part->x > part->viewport_width - g->width)
-         width -= (part->x - (part->viewport_width - g->width));
+    // if game is paused, also pause playfield and door animations
+    if (isPausedOnPlayfieldOrDoor(part))
+      part->initial_anim_sync_frame++;
 
-       if (part->y < 0)
-       {
-         dst_y = 0;
-         height += part->y;
-         cut_y = -part->y;
-       }
-       else if (part->y > part->viewport_height - g->height)
-         height -= (part->y - (part->viewport_height - g->height));
+    sync_frame = anim_sync_frame - part->initial_anim_sync_frame;
 
-       if (width <= 0 || height <= 0)
-         continue;
+    // re-initialize random animation frame after animation delay
+    if (g->anim_mode == ANIM_RANDOM &&
+       sync_frame % g->anim_delay == 0 &&
+       sync_frame > 0)
+      part->anim_random_frame = GetSimpleRandom(g->anim_frames);
 
-       dst_x += part->viewport_x;
-       dst_y += part->viewport_y;
+    gfx.anim_random_frame = part->anim_random_frame;
 
-       sync_frame = anim_sync_frame - part->initial_anim_sync_frame;
-       frame = getAnimationFrame(g->anim_frames, g->anim_delay,
-                                 g->anim_mode, g->anim_start_frame,
-                                 sync_frame);
+    frame = getAnimationFrame(g->anim_frames, g->anim_delay,
+                             g->anim_mode, g->anim_start_frame,
+                             sync_frame);
 
-       getFixedGraphicSource(part->graphic, frame, &src_bitmap,
-                             &src_x, &src_y);
+    gfx.anim_random_frame = last_anim_random_frame;
 
-       src_x += cut_x;
-       src_y += cut_y;
+    getGlobalAnimGraphicSource(part->graphic, frame, &src_bitmap,
+                              &src_x, &src_y);
 
-       if (drawing_target == DRAW_TO_SCREEN)
-         blit_screen(src_bitmap, src_x, src_y, width, height,
-                     dst_x, dst_y);
-       else
-         blit_bitmap(src_bitmap, fade_bitmap, src_x, src_y, width, height,
-                     dst_x, dst_y);
-      }
-    }
+    BlitGlobalAnimation(part, src_bitmap, src_x, src_y, drawing_target);
   }
 
   if (drawing_target == DRAW_TO_FADE_TARGET)
@@ -865,6 +994,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
 
   part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_1;
 
+  part->class_playfield_or_door = FALSE;
+
   if (part->control_info.class == get_hash_from_key("window") ||
       part->control_info.class == get_hash_from_key("border"))
   {
@@ -890,7 +1021,7 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
     viewport_width  = part->graphic_info.width;
     viewport_height = part->graphic_info.height;
 
-    part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_2;
+    part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_3;
 
     // do not use global animation mouse pointer when reloading artwork
     if (global.anim_status != GAME_MODE_LOADING)
@@ -902,6 +1033,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
     viewport_y = DY;
     viewport_width  = DXSIZE;
     viewport_height = DYSIZE;
+
+    part->class_playfield_or_door = TRUE;
   }
   else if (part->control_info.class == get_hash_from_key("door_2"))
   {
@@ -919,6 +1052,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
       viewport_width  = VXSIZE;
       viewport_height = VYSIZE;
     }
+
+    part->class_playfield_or_door = TRUE;
   }
   else         // default: "playfield"
   {
@@ -926,6 +1061,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
     viewport_y = REAL_SY;
     viewport_width  = FULL_SXSIZE;
     viewport_height = FULL_SYSIZE;
+
+    part->class_playfield_or_door = TRUE;
   }
 
   if (viewport_x != part->viewport_x ||
@@ -963,7 +1100,7 @@ static void PlayGlobalAnimSound(struct GlobalAnimPartControlInfo *part)
     PlaySound(sound);
 
 #if 0
-  printf("::: PLAY SOUND %d.%d.%d: %d\n",
+  Debug("anim:PlayGlobalAnimSound", "PLAY SOUND %d.%d.%d: %d",
         part->anim_nr, part->nr, part->mode_nr, sound);
 #endif
 }
@@ -978,8 +1115,8 @@ static void StopGlobalAnimSound(struct GlobalAnimPartControlInfo *part)
   StopSound(sound);
 
 #if 0
-  printf("::: STOP SOUND %d.%d.%d: %d\n",
-        part->anim_nr, part->nr, part->mode_nr, sound);
+  Debug("anim:StopGlobalAnimSound", "STOP SOUND %d.%d.%d: %d",
+       part->anim_nr, part->nr, part->mode_nr, sound);
 #endif
 }
 
@@ -999,8 +1136,8 @@ static void PlayGlobalAnimMusic(struct GlobalAnimPartControlInfo *part)
     PlayMusic(music);
 
 #if 0
-  printf("::: PLAY MUSIC %d.%d.%d: %d\n",
-        part->anim_nr, part->nr, part->mode_nr, music);
+  Debug("anim:PlayGlobalAnimMusic", "PLAY MUSIC %d.%d.%d: %d",
+       part->anim_nr, part->nr, part->mode_nr, music);
 #endif
 }
 
@@ -1011,11 +1148,18 @@ static void StopGlobalAnimMusic(struct GlobalAnimPartControlInfo *part)
   if (music == MUS_UNDEFINED)
     return;
 
+  char *anim_music = getMusicInfoEntryFilename(music);
+  char *curr_music = getCurrentlyPlayingMusicFilename();
+
+  // do not stop music if global anim music differs from current music
+  if (!strEqual(curr_music, anim_music))
+    return;
+
   StopMusic();
 
 #if 0
-  printf("::: STOP MUSIC %d.%d.%d: %d\n",
-        part->anim_nr, part->nr, part->mode_nr, music);
+  Debug("anim:StopGlobalAnimMusic", "STOP MUSIC %d.%d.%d: %d",
+       part->anim_nr, part->nr, part->mode_nr, music);
 #endif
 }
 
@@ -1060,6 +1204,7 @@ static void PlayGlobalAnimSoundIfLoop(struct GlobalAnimPartControlInfo *part)
 static boolean checkGlobalAnimEvent(int anim_event, int mask)
 {
   int mask_anim_only = mask & ~ANIM_EVENT_PART_MASK;
+  int mask_ce_only   = mask & ~ANIM_EVENT_PAGE_MASK;
 
   if (mask & ANIM_EVENT_ANY)
     return (anim_event & ANIM_EVENT_ANY);
@@ -1067,6 +1212,9 @@ static boolean checkGlobalAnimEvent(int anim_event, int mask)
     return (anim_event & ANIM_EVENT_SELF);
   else if (mask & ANIM_EVENT_UNCLICK_ANY)
     return (anim_event & ANIM_EVENT_UNCLICK_ANY);
+  else if (mask & ANIM_EVENT_CE_CHANGE)
+    return (anim_event == mask ||
+           anim_event == mask_ce_only);
   else
     return (anim_event == mask ||
            anim_event == mask_anim_only);
@@ -1075,62 +1223,116 @@ static boolean checkGlobalAnimEvent(int anim_event, int mask)
 static boolean isClickablePart(struct GlobalAnimPartControlInfo *part, int mask)
 {
   struct GraphicInfo *c = &part->control_info;
-  int num_init_events = GetGlobalAnimEventValueCount(c->init_event);
-  int num_anim_events = GetGlobalAnimEventValueCount(c->anim_event);
   int i;
 
-  for (i = 0; i < num_init_events; i++)
+  if (part->init_event_state)
   {
-    int init_event = GetGlobalAnimEventValue(c->init_event, i);
+    int num_init_events = GetGlobalAnimEventValueCount(c->init_event);
 
-    if (checkGlobalAnimEvent(init_event, mask))
-      return TRUE;
-  }
+    for (i = 0; i < num_init_events; i++)
+    {
+      int init_event = GetGlobalAnimEventValue(c->init_event, i);
 
-  for (i = 0; i < num_anim_events; i++)
+      if (checkGlobalAnimEvent(init_event, mask))
+       return TRUE;
+    }
+  }
+  else if (part->anim_event_state)
   {
-    int anim_event = GetGlobalAnimEventValue(c->anim_event, i);
+    int num_anim_events = GetGlobalAnimEventValueCount(c->anim_event);
 
-    if (checkGlobalAnimEvent(anim_event, mask))
-      return TRUE;
+    for (i = 0; i < num_anim_events; i++)
+    {
+      int anim_event = GetGlobalAnimEventValue(c->anim_event, i);
+
+      if (checkGlobalAnimEvent(anim_event, mask))
+       return TRUE;
+    }
   }
 
   return FALSE;
 }
 
-static boolean isClickedPart(struct GlobalAnimPartControlInfo *part,
-                            int mx, int my, boolean clicked)
+static boolean isInsidePartStacked(struct GlobalAnimPartControlInfo *part,
+                                  int mx, int my)
 {
   struct GraphicInfo *g = &part->graphic_info;
+  struct GraphicInfo *c = &part->control_info;
   int part_x = part->viewport_x + part->x;
   int part_y = part->viewport_y + part->y;
   int part_width  = g->width;
   int part_height = g->height;
+  int x, y;
+
+  for (y = 0; y < c->stacked_yfactor; y++)
+  {
+    for (x = 0; x < c->stacked_xfactor; x++)
+    {
+      int part_stacked_x = part_x + x * (part_width  + c->stacked_xoffset);
+      int part_stacked_y = part_y + y * (part_height + c->stacked_yoffset);
+
+      if (mx >= part_stacked_x &&
+         mx <  part_stacked_x + part_width &&
+         my >= part_stacked_y &&
+         my <  part_stacked_y + part_height)
+       return TRUE;
+    }
+  }
 
+  return FALSE;
+}
+
+static boolean isClickedPart(struct GlobalAnimPartControlInfo *part,
+                            int mx, int my, boolean clicked)
+{
   // check if mouse click was detected at all
   if (!clicked)
     return FALSE;
 
-  // check if mouse click is inside the animation part's viewport
+  // check if mouse click is outside the animation part's viewport
   if (mx <  part->viewport_x ||
       mx >= part->viewport_x + part->viewport_width ||
       my <  part->viewport_y ||
       my >= part->viewport_y + part->viewport_height)
     return FALSE;
 
-  // check if mouse click is inside the animation part's graphic
-  if (mx <  part_x ||
-      mx >= part_x + part_width ||
-      my <  part_y ||
-      my >= part_y + part_height)
-    return FALSE;
+  // check if mouse click is inside the animation part's (stacked) graphic
+  if (isInsidePartStacked(part, mx, my))
+    return TRUE;
 
-  return TRUE;
+  return FALSE;
+}
+
+static boolean clickBlocked(struct GlobalAnimPartControlInfo *part)
+{
+  return ((part->control_info.style & STYLE_BLOCK) ? TRUE : FALSE);
 }
 
 static boolean clickConsumed(struct GlobalAnimPartControlInfo *part)
 {
-  return (part->control_info.style & STYLE_PASSTHROUGH ? FALSE : TRUE);
+  return ((part->control_info.style & STYLE_PASSTHROUGH) ? FALSE : TRUE);
+}
+
+static void SetGlobalAnimPartTileXY(struct GlobalAnimPartControlInfo *part)
+{
+  // calculate playfield position (with scrolling) for related CE tile
+  // (do not use FX/FY, which are incorrect during envelope requests)
+  int FX0 = 2 * TILEX_VAR;     // same as FX during DRAW_TO_FIELDBUFFER
+  int FY0 = 2 * TILEY_VAR;     // same as FY during DRAW_TO_FIELDBUFFER
+  int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
+  int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
+  int sx = FX0 + SCREENX(part->tile_x) * TILEX_VAR;
+  int sy = FY0 + SCREENY(part->tile_y) * TILEY_VAR;
+  int cx = SX - REAL_SX;
+  int cy = SY - REAL_SY;
+  int x = sx - fx + cx;
+  int y = sy - fy + cy;
+
+  part->tile_xoffset += part->step_xoffset;
+  part->tile_yoffset += part->step_yoffset;
+
+  part->x = x + part->tile_xoffset;
+  part->y = y + part->tile_yoffset;
 }
 
 static void InitGlobalAnim_Triggered(struct GlobalAnimPartControlInfo *part,
@@ -1158,7 +1360,7 @@ static void InitGlobalAnim_Triggered(struct GlobalAnimPartControlInfo *part,
     {
       struct GlobalAnimPartControlInfo *part2 = &anim2->part[part2_nr];
 
-      if (!(part2->state & ANIM_STATE_RUNNING))
+      if (!(part2->state & (ANIM_STATE_RUNNING | ANIM_STATE_WAITING)))
        continue;
 
       if (isClickablePart(part2, mask))
@@ -1167,13 +1369,15 @@ static void InitGlobalAnim_Triggered(struct GlobalAnimPartControlInfo *part,
        *click_consumed |= clickConsumed(part);         // click was on "part"!
 
 #if DEBUG_ANIM_EVENTS
-       printf("::: => %d.%d TRIGGERED BY %s OF %d.%d\n",
-              part2->old_anim_nr + 1, part2->old_nr + 1, info_text,
-              part->old_anim_nr + 1, part->old_nr + 1);
+       Debug("anim:InitGlobalAnim_Triggered",
+             "%d.%d TRIGGERED BY %s OF %d.%d",
+             part2->old_anim_nr + 1, part2->old_nr + 1, info_text,
+             part->old_anim_nr + 1, part->old_nr + 1);
 #endif
 #if 0
-       printf("::: %d.%d TRIGGER CLICKED [%d]\n", anim2_nr, part2_nr,
-              part2->control_info.anim_event_action);
+       Debug("anim:InitGlobalAnim_Triggered",
+             "%d.%d TRIGGER CLICKED [%d]", anim2_nr, part2_nr,
+             part2->control_info.anim_event_action);
 #endif
 
        // after executing event action, ignore any further actions
@@ -1184,15 +1388,77 @@ static void InitGlobalAnim_Triggered(struct GlobalAnimPartControlInfo *part,
 #if 0
       struct GraphicInfo *c = &part2->control_info;
 
-      printf("::: - %d.%d: 0x%08x, 0x%08x [0x%08x]",
-            anim2_nr, part2_nr, c->init_event, c->anim_event, mask);
-
       if (isClickablePart(part2, mask))
-       printf(" <--- TRIGGERED BY %d.%d",
-              anim_nr, part_nr);
+       Debug("anim:InitGlobalAnim_Triggered",
+             "%d.%d: 0x%08x, 0x%08x [0x%08x] <--- TRIGGERED BY %d.%d",
+             anim2_nr, part2_nr, c->init_event, c->anim_event, mask,
+             anim_nr, part_nr);
+      else
+       Debug("anim:InitGlobalAnim_Triggered",
+             "%d.%d: 0x%08x, 0x%08x [0x%08x]",
+             anim2_nr, part2_nr, c->init_event, c->anim_event, mask);
+#endif
+    }
+  }
+}
+
+static void InitGlobalAnim_Triggered_ByCustomElement(int nr, int page,
+                                                    int x, int y,
+                                                    int trigger_x,
+                                                    int trigger_y)
+{
+  struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[GAME_MODE_PLAYING];
+
+  int event_value = ANIM_EVENT_CE_CHANGE;
+  int event_bits = (nr << ANIM_EVENT_CE_BIT) | (page << ANIM_EVENT_PAGE_BIT);
+  int mask = event_value | event_bits;
+  int anim2_nr;
 
-      printf("\n");
+  for (anim2_nr = 0; anim2_nr < ctrl->num_anims; anim2_nr++)
+  {
+    struct GlobalAnimMainControlInfo *anim2 = &ctrl->anim[anim2_nr];
+    int part2_nr;
+
+    for (part2_nr = 0; part2_nr < anim2->num_parts_all; part2_nr++)
+    {
+      struct GlobalAnimPartControlInfo *part2 = &anim2->part[part2_nr];
+
+      if (!(part2->state & (ANIM_STATE_RUNNING | ANIM_STATE_WAITING)))
+       continue;
+
+      if (isClickablePart(part2, mask) && !part2->triggered)
+      {
+       struct GraphicInfo *c = &part2->control_info;
+
+       if (c->position == POS_CE ||
+           c->position == POS_CE_TRIGGER)
+       {
+         // store CE tile and offset position to handle scrolling
+         part2->tile_x = (c->position == POS_CE_TRIGGER ? trigger_x : x);
+         part2->tile_y = (c->position == POS_CE_TRIGGER ? trigger_y : y);
+         part2->tile_xoffset = c->x;
+         part2->tile_yoffset = c->y;
+
+         // set resulting animation position relative to CE tile position
+         // (but only for ".init_event", not ".anim_event" type events)
+         if (part2->init_event_state)
+           SetGlobalAnimPartTileXY(part2);
+
+         // restart animation (by using current sync frame)
+         part2->initial_anim_sync_frame = anim_sync_frame;
+       }
+
+       part2->triggered = TRUE;
+
+       // do not trigger any other animation if CE change event was consumed
+       if (c->style == STYLE_CONSUME_CE_EVENT)
+         return;
+
+#if 0
+       Debug("anim:InitGlobalAnim_Triggered_ByCustomElement",
+             "%d.%d TRIGGERED BY CE %d", anim2_nr, part2_nr, nr + 1);
 #endif
+      }
     }
   }
 }
@@ -1201,7 +1467,8 @@ static void HandleGlobalAnimDelay(struct GlobalAnimPartControlInfo *part,
                                  int delay_type, char *info_text)
 {
 #if DEBUG_ANIM_DELAY
-  printf("::: %d.%d %s\n", part->old_anim_nr + 1, part->old_nr + 1, info_text);
+  Debug("anim:HandleGlobalAnimDelay", "%d.%d %s",
+       part->old_anim_nr + 1, part->old_nr + 1, info_text);
 #endif
 
   DoGlobalAnim_DelayAction(part, delay_type);
@@ -1211,7 +1478,8 @@ static void HandleGlobalAnimEvent(struct GlobalAnimPartControlInfo *part,
                                  int event_value, char *info_text)
 {
 #if DEBUG_ANIM_EVENTS
-  printf("::: %d.%d %s\n", part->old_anim_nr + 1, part->old_nr + 1, info_text);
+  Debug("anim:HandleGlobalAnimEvent", "%d.%d %s",
+       part->old_anim_nr + 1, part->old_nr + 1, info_text);
 #endif
 
   boolean click_consumed = FALSE;
@@ -1233,6 +1501,11 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
   struct GraphicInfo *g = &part->graphic_info;
   struct GraphicInfo *c = &part->control_info;
   boolean viewport_changed = SetGlobalAnimPart_Viewport(part);
+  int alpha = (g->alpha != -1 ? g->alpha : SDL_ALPHA_OPAQUE);
+
+  // if game is paused, also pause playfield and door animations
+  if (isPausedOnPlayfieldOrDoor(part))
+    return state;
 
   if (viewport_changed)
     state |= ANIM_STATE_RESTART;
@@ -1258,7 +1531,24 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
     part->anim_event_state = (c->anim_event != ANIM_EVENT_UNDEFINED);
 
     part->initial_anim_sync_frame =
-      (g->anim_global_sync ? 0 : anim_sync_frame + part->init_delay_counter);
+      (g->anim_global_sync || g->anim_global_anim_sync ? 0 :
+       anim_sync_frame + part->init_delay_counter);
+
+    // do not re-initialize random animation frame after fade-in
+    if (part->anim_random_frame == -1)
+      part->anim_random_frame = GetSimpleRandom(g->anim_frames);
+
+    if (c->fade_mode & FADE_MODE_FADE)
+    {
+      // when fading in screen, first frame is 100 % transparent or opaque
+      part->fade_delay_counter = c->fade_delay + 1;
+      part->fade_alpha = (c->fade_mode == FADE_MODE_FADE_IN ? 0 : alpha);
+    }
+    else
+    {
+      part->fade_delay_counter = 0;
+      part->fade_alpha = -1;
+    }
 
     if (c->direction & MV_HORIZONTAL)
     {
@@ -1362,7 +1652,7 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
       part->init_event_state)
   {
     if (part->initial_anim_sync_frame > 0)
-      part->initial_anim_sync_frame -= part->init_delay_counter - 1;
+      part->initial_anim_sync_frame = anim_sync_frame;
 
     part->init_delay_counter = 1;
     part->init_event_state = FALSE;
@@ -1391,9 +1681,13 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
 
       HandleGlobalAnimDelay(part, ANIM_DELAY_INIT,  "START [INIT_DELAY]");
       HandleGlobalAnimEvent(part, ANIM_EVENT_START, "START [ANIM]");
-    }
 
-    return ANIM_STATE_WAITING;
+      // continue with state ANIM_STATE_RUNNING (set below)
+    }
+    else
+    {
+      return ANIM_STATE_WAITING;
+    }
   }
 
   if (part->init_event_state)
@@ -1469,11 +1763,18 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
     return ANIM_STATE_WAITING;
   }
 
+  if (part->fade_delay_counter > 0)
+  {
+    part->fade_delay_counter--;
+    part->fade_alpha = alpha * (c->fade_mode == FADE_MODE_FADE_IN ?
+                               c->fade_delay - part->fade_delay_counter :
+                               part->fade_delay_counter) / c->fade_delay;
+  }
+
   // special case to prevent expiring loop sounds when playing
   PlayGlobalAnimSoundIfLoop(part);
 
-  if (!DelayReachedExt(&part->step_delay, part->step_delay_value,
-                      anim_sync_frame))
+  if (!DelayReachedExt(&part->step_delay, anim_sync_frame))
     return ANIM_STATE_RUNNING;
 
 #if 0
@@ -1481,15 +1782,23 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
     static unsigned int last_counter = -1;
     unsigned int counter = Counter();
 
-    printf("::: NEXT ANIM PART [%d, %d]\n",
-          anim_sync_frame, counter - last_counter);
+    Debug("anim:HandleGlobalAnim_Part", "NEXT ANIM PART [%d, %d]",
+         anim_sync_frame, counter - last_counter);
 
     last_counter = counter;
   }
 #endif
 
-  part->x += part->step_xoffset;
-  part->y += part->step_yoffset;
+  if (c->position == POS_CE ||
+      c->position == POS_CE_TRIGGER)
+  {
+    SetGlobalAnimPartTileXY(part);
+  }
+  else
+  {
+    part->x += part->step_xoffset;
+    part->y += part->step_yoffset;
+  }
 
   anim->last_x = part->x;
   anim->last_y = part->y;
@@ -1507,21 +1816,22 @@ static void HandleGlobalAnim_Main(struct GlobalAnimMainControlInfo *anim,
   int i;
 
 #if 0
-  printf("::: HandleGlobalAnim_Main: %d, %d => %d\n",
+  Debug("anim:HandleGlobalAnim_Main", "%d, %d => %d",
         anim->mode_nr, anim->nr, anim->num_parts);
-  printf("::: %d, %d, %d\n", global.num_toons, setup.toons, num_toons);
+  Debug("anim:HandleGlobalAnim_Main",
+       "%d, %d, %d", global.num_toons, setup.toons, num_toons);
 #endif
 
 #if 0
-  printf("::: %s(%d): %d, %d, %d [%d]\n",
-        (action == ANIM_START ? "ANIM_START" :
-         action == ANIM_CONTINUE ? "ANIM_CONTINUE" :
-         action == ANIM_STOP ? "ANIM_STOP" : "(should not happen)"),
-        anim->nr,
-        anim->state & ANIM_STATE_RESTART,
-        anim->state & ANIM_STATE_WAITING,
-        anim->state & ANIM_STATE_RUNNING,
-        anim->num_parts);
+  Debug("anim:HandleGlobalAnim_Main", "%s(%d): %d, %d, %d [%d]",
+       (action == ANIM_START ? "ANIM_START" :
+        action == ANIM_CONTINUE ? "ANIM_CONTINUE" :
+        action == ANIM_STOP ? "ANIM_STOP" : "(should not happen)"),
+       anim->nr,
+       anim->state & ANIM_STATE_RESTART,
+       anim->state & ANIM_STATE_WAITING,
+       anim->state & ANIM_STATE_RUNNING,
+       anim->num_parts);
 #endif
 
   switch (action)
@@ -1557,8 +1867,8 @@ static void HandleGlobalAnim_Main(struct GlobalAnimMainControlInfo *anim,
   if (c->anim_mode & ANIM_ALL || anim->num_parts == 0)
   {
 #if 0
-    printf("::: HandleGlobalAnim_Main: %d, %d => %d\n",
-          anim->mode_nr, anim->nr, num_parts);
+    Debug("anim:HandleGlobalAnim_Main", "%d, %d => %d",
+         anim->mode_nr, anim->nr, num_parts);
 #endif
 
     for (i = 0; i < num_parts; i++)
@@ -1611,9 +1921,13 @@ static void HandleGlobalAnim_Main(struct GlobalAnimMainControlInfo *anim,
   for (i = 0; i < num_parts; i++)
     anim->part[i].state = ANIM_STATE_INACTIVE;
 
-  // ... then set current animation parts to "running"
+  // ... then set current animation part to "running" ...
   part->state = ANIM_STATE_RUNNING;
 
+  // ... unless it is waiting for an initial event
+  if (part->init_event_state)
+    part->state = ANIM_STATE_WAITING;
+
   anim->state = HandleGlobalAnim_Part(part, anim->state);
 
   if (anim->state & ANIM_STATE_RESTART)
@@ -1655,8 +1969,7 @@ static void HandleGlobalAnim_Mode(struct GlobalAnimControlInfo *ctrl, int action
   int i;
 
 #if 0
-  printf("::: HandleGlobalAnim_Mode: %d => %d\n",
-        ctrl->nr, ctrl->num_anims);
+  Debug("anim:HandleGlobalAnim_Mode", "%d => %d", ctrl->nr, ctrl->num_anims);
 #endif
 
   for (i = 0; i < ctrl->num_anims; i++)
@@ -1666,7 +1979,7 @@ static void HandleGlobalAnim_Mode(struct GlobalAnimControlInfo *ctrl, int action
 static void HandleGlobalAnim(int action, int game_mode)
 {
 #if 0
-  printf("::: HandleGlobalAnim [mode == %d]\n", game_status);
+  Debug("anim:HandleGlobalAnim", "mode == %d", game_status);
 #endif
 
   HandleGlobalAnim_Mode(&global_anim_ctrl[game_mode], action);
@@ -1677,7 +1990,7 @@ static void DoAnimationExt(void)
   int i;
 
 #if 0
-  printf("::: DoAnimation [%d, %d]\n", anim_sync_frame, Counter());
+  Debug("anim:DoAnimationExt", "%d, %d", anim_sync_frame, Counter());
 #endif
 
   // global animations now synchronized with frame delay of screen update
@@ -1716,7 +2029,10 @@ static boolean DoGlobalAnim_EventAction(struct GlobalAnimPartControlInfo *part)
   if (event_action == ANIM_EVENT_ACTION_NONE)
     return FALSE;
 
-  PushUserEvent(USEREVENT_ANIM_EVENT_ACTION, event_action, 0);
+  if (event_action < MAX_IMAGE_FILES)
+    PushUserEvent(USEREVENT_ANIM_EVENT_ACTION, event_action, 0);
+  else
+    OpenURLFromHash(anim_url_hash, event_action);
 
   // check if further actions are allowed to be executed
   if (part->control_info.style & STYLE_MULTIPLE_ACTIONS)
@@ -1727,29 +2043,17 @@ static boolean DoGlobalAnim_EventAction(struct GlobalAnimPartControlInfo *part)
 
 static void InitGlobalAnim_Clickable(void)
 {
-  int mode_nr;
+  int i;
 
-  for (mode_nr = 0; mode_nr < NUM_GAME_MODES; mode_nr++)
+  for (i = 0; i < num_global_anim_list; i++)
   {
-    struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr];
-    int anim_nr;
+    struct GlobalAnimPartControlInfo *part = global_anim_list[i];
 
-    for (anim_nr = 0; anim_nr < ctrl->num_anims; anim_nr++)
-    {
-      struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr];
-      int part_nr;
+    if (part->triggered)
+      part->clicked = TRUE;
 
-      for (part_nr = 0; part_nr < anim->num_parts_all; part_nr++)
-      {
-       struct GlobalAnimPartControlInfo *part = &anim->part[part_nr];
-
-       if (part->triggered)
-         part->clicked = TRUE;
-
-       part->triggered = FALSE;
-       part->clickable = FALSE;
-      }
-    }
+    part->triggered = FALSE;
+    part->clickable = FALSE;
   }
 }
 
@@ -1763,99 +2067,93 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
   boolean anything_clicked = FALSE;
   boolean any_part_clicked = FALSE;
   boolean any_event_action = FALSE;
-  int mode_nr;
+  int i;
 
-  // check game modes in reverse draw order (to stop when clicked)
-  for (mode_nr = NUM_GAME_MODES - 1; mode_nr >= 0; mode_nr--)
+  // check animation parts in reverse draw order (to stop when clicked)
+  for (i = num_global_anim_list - 1; i >= 0; i--)
   {
-    struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr];
-    int anim_nr;
+    struct GlobalAnimPartControlInfo *part = global_anim_list[i];
 
-    // check animations in reverse draw order (to stop when clicked)
-    for (anim_nr = ctrl->num_anims - 1; anim_nr >= 0; anim_nr--)
-    {
-      struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr];
-      int part_nr;
-
-      // check animation parts in reverse draw order (to stop when clicked)
-      for (part_nr = anim->num_parts_all - 1; part_nr >= 0; part_nr--)
-      {
-       struct GlobalAnimPartControlInfo *part = &anim->part[part_nr];
+    // if request dialog is active, only handle pointer-style animations
+    if (game.request_active &&
+       part->control_info.class != get_hash_from_key("pointer"))
+      continue;
 
-       if (clicked_event == ANIM_CLICKED_RESET)
-       {
-         part->clicked = FALSE;
+    if (clicked_event == ANIM_CLICKED_RESET)
+    {
+      part->clicked = FALSE;
 
-         continue;
-       }
+      continue;
+    }
 
-       if (!part->clickable)
-         continue;
+    if (!part->clickable)
+      continue;
 
-       if (!(part->state & ANIM_STATE_RUNNING))
-         continue;
+    if (!(part->state & ANIM_STATE_RUNNING))
+      continue;
 
-       // always handle "any" click events (clicking anywhere on screen) ...
-       if (clicked_event == ANIM_CLICKED_PRESSED &&
-           isClickablePart(part, ANIM_EVENT_ANY))
-       {
+    // always handle "any" click events (clicking anywhere on screen) ...
+    if (clicked_event == ANIM_CLICKED_PRESSED &&
+       isClickablePart(part, ANIM_EVENT_ANY))
+    {
 #if DEBUG_ANIM_EVENTS
-         printf("::: => %d.%d TRIGGERED BY ANY\n",
-                part->old_anim_nr + 1, part->old_nr + 1);
+      Debug("anim:InitGlobalAnim_Clicked", "%d.%d TRIGGERED BY ANY",
+           part->old_anim_nr + 1, part->old_nr + 1);
 #endif
 
-         anything_clicked = part->clicked = TRUE;
-         click_consumed |= clickConsumed(part);
-       }
+      anything_clicked = part->clicked = TRUE;
+      click_consumed |= clickConsumed(part);
+    }
 
-       // always handle "unclick:any" events (releasing anywhere on screen) ...
-       if (clicked_event == ANIM_CLICKED_RELEASED &&
-           isClickablePart(part, ANIM_EVENT_UNCLICK_ANY))
-       {
+    // always handle "unclick:any" events (releasing anywhere on screen) ...
+    if (clicked_event == ANIM_CLICKED_RELEASED &&
+       isClickablePart(part, ANIM_EVENT_UNCLICK_ANY))
+    {
 #if DEBUG_ANIM_EVENTS
-         printf("::: => %d.%d TRIGGERED BY UNCLICK:ANY\n",
-                part->old_anim_nr + 1, part->old_nr + 1);
+      Debug("anim:InitGlobalAnim_Clicked", "%d.%d TRIGGERED BY UNCLICK:ANY",
+           part->old_anim_nr + 1, part->old_nr + 1);
 #endif
 
-         anything_clicked = part->clicked = TRUE;
-         click_consumed |= clickConsumed(part);
-       }
+      anything_clicked = part->clicked = TRUE;
+      click_consumed |= clickConsumed(part);
+    }
 
-       // ... but only handle the first (topmost) clickable animation
-       if (any_part_clicked)
-         continue;
+    // ... but only handle the first (topmost) clickable animation
+    if (any_part_clicked)
+      continue;
 
-       if (clicked_event == ANIM_CLICKED_PRESSED &&
-           isClickedPart(part, mx, my, TRUE))
-       {
+    if (clicked_event == ANIM_CLICKED_PRESSED &&
+       isClickedPart(part, mx, my, TRUE))
+    {
 #if 0
-         printf("::: %d.%d CLICKED [%d]\n", anim_nr, part_nr,
-                part->control_info.anim_event_action);
+      Debug("anim:InitGlobalAnim_Clicked", "%d.%d CLICKED [%d]",
+           anim_nr, part_nr, part->control_info.anim_event_action);
 #endif
 
-         // after executing event action, ignore any further actions
-         if (!any_event_action && DoGlobalAnim_EventAction(part))
-           any_event_action = TRUE;
+      // after executing event action, ignore any further actions
+      if (!any_event_action && DoGlobalAnim_EventAction(part))
+       any_event_action = TRUE;
 
-         // determine if mouse clicks should be blocked from other animations
-         any_part_clicked |= clickConsumed(part);
+      // determine if mouse clicks should be blocked from other animations
+      any_part_clicked |= clickConsumed(part);
 
-         if (isClickablePart(part, ANIM_EVENT_SELF))
-         {
+      if (isClickablePart(part, ANIM_EVENT_SELF))
+      {
 #if DEBUG_ANIM_EVENTS
-           printf("::: => %d.%d TRIGGERED BY SELF\n",
-                  part->old_anim_nr + 1, part->old_nr + 1);
+       Debug("anim:InitGlobalAnim_Clicked", "%d.%d TRIGGERED BY SELF",
+             part->old_anim_nr + 1, part->old_nr + 1);
 #endif
 
-           anything_clicked = part->clicked = TRUE;
-           click_consumed |= clickConsumed(part);
-         }
-
-         // check if this click is defined to trigger other animations
-         InitGlobalAnim_Triggered(part, &click_consumed, &any_event_action,
-                                  ANIM_EVENT_CLICK, "CLICK");
-       }
+       anything_clicked = part->clicked = TRUE;
+       click_consumed |= clickConsumed(part);
       }
+
+      // determine if mouse clicks should be blocked by this animation
+      click_consumed |= clickBlocked(part);
+
+      // check if this click is defined to trigger other animations
+      InitGlobalAnim_Triggered(part, &click_consumed, &any_event_action,
+                              ANIM_EVENT_CLICK, "CLICK");
     }
   }
 
@@ -1863,7 +2161,8 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
   {
     handle_click = TRUE;
 
-    HandleGlobalAnim(ANIM_CONTINUE, game_status);
+    for (i = 0; i < NUM_GAME_MODES; i++)
+      HandleGlobalAnim(ANIM_CONTINUE, i);
 
     handle_click = FALSE;
 
@@ -1884,6 +2183,23 @@ static void ResetGlobalAnim_Clicked(void)
   InitGlobalAnim_Clicked(-1, -1, ANIM_CLICKED_RESET);
 }
 
+void RestartGlobalAnimsByStatus(int status)
+{
+  int anim_status_last = global.anim_status;
+
+  global.anim_status = status;
+
+  // force restarting global animations by changed global animation status
+  DrawGlobalAnimationsExt(DRAW_TO_SCREEN, DRAW_GLOBAL_ANIM_STAGE_RESTART);
+
+  global.anim_status = anim_status_last;
+}
+
+void SetAnimStatusBeforeFading(int status)
+{
+  anim_status_last_before_fading = status;
+}
+
 boolean HandleGlobalAnimClicks(int mx, int my, int button, boolean force_click)
 {
   static boolean click_consumed = FALSE;
@@ -1914,3 +2230,19 @@ boolean HandleGlobalAnimClicks(int mx, int my, int button, boolean force_click)
 
   return click_consumed_current;
 }
+
+int getGlobalAnimSyncFrame(void)
+{
+  return anim_sync_frame;
+}
+
+void HandleGlobalAnimEventByElementChange(int element, int page, int x, int y,
+                                         int trigger_x, int trigger_y)
+{
+  if (!IS_CUSTOM_ELEMENT(element))
+    return;
+
+  // custom element stored as 0 to 255, change page stored as 1 to 32
+  InitGlobalAnim_Triggered_ByCustomElement(element - EL_CUSTOM_START, page + 1,
+                                          x, y, trigger_x, trigger_y);
+}