removed unnecessary screen redraws when waiting for events to continue
[rocksndiamonds.git] / src / anim.c
index eefb5a1dba076afd59842c4f05459dec1ab1e465..01434554fc94a36b62ba966044442355baef48ee 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
 // ============================================================================
@@ -19,6 +19,7 @@
 #include "screens.h"
 
 
+#define DEBUG_ANIM_DELAY               0
 #define DEBUG_ANIM_EVENTS              0
 
 
@@ -107,6 +108,7 @@ struct GlobalAnimPartControlInfo
   int step_xoffset, step_yoffset;
 
   unsigned int initial_anim_sync_frame;
+  unsigned int anim_random_frame;
   unsigned int step_delay, step_delay_value;
 
   int init_delay_counter;
@@ -209,6 +211,7 @@ struct AnimClassGameMode
 };
 
 // forward declaration for internal use
+static void DoGlobalAnim_DelayAction(struct GlobalAnimPartControlInfo *, int);
 static boolean DoGlobalAnim_EventAction(struct GlobalAnimPartControlInfo *);
 static void HandleGlobalAnim(int, int);
 static void DoAnimationExt(void);
@@ -228,6 +231,8 @@ static int anim_classes_last = ANIM_CLASS_NONE;
 
 static boolean drawing_to_fading_buffer = FALSE;
 
+static boolean handle_click = FALSE;
+
 
 // ============================================================================
 // generic animation frame calculation
@@ -238,6 +243,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
@@ -405,6 +413,7 @@ 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;
@@ -485,13 +494,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;
@@ -509,6 +520,7 @@ 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;
@@ -532,6 +544,20 @@ static void InitGlobalAnimControls(void)
          anim->base = *part;
          anim->has_base = TRUE;
        }
+
+       // apply special settings for 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;
+       }
       }
 
       if (anim->num_parts > 0 || anim->has_base)
@@ -746,6 +772,7 @@ static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
          (g->draw_masked ? BlitBitmapMasked : BlitBitmap);
        void (*blit_screen)(Bitmap *, int, int, int, int, int, int) =
          (g->draw_masked ? BlitToScreenMasked : BlitToScreen);
+       int last_anim_random_frame = gfx.anim_random_frame;
 
        if (!(part->state & ANIM_STATE_RUNNING))
          continue;
@@ -778,10 +805,21 @@ static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
        dst_y += part->viewport_y;
 
        sync_frame = anim_sync_frame - part->initial_anim_sync_frame;
+
+       // 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);
+
+       gfx.anim_random_frame = part->anim_random_frame;
+
        frame = getAnimationFrame(g->anim_frames, g->anim_delay,
                                  g->anim_mode, g->anim_start_frame,
                                  sync_frame);
 
+       gfx.anim_random_frame = last_anim_random_frame;
+
        getFixedGraphicSource(part->graphic, frame, &src_bitmap,
                              &src_x, &src_y);
 
@@ -811,13 +849,24 @@ static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
 
 void DrawGlobalAnimations(int drawing_target, int drawing_stage)
 {
+  int last_cursor_mode_override = gfx.cursor_mode_override;
+
   if (drawing_stage == DRAW_GLOBAL_ANIM_STAGE_1)
+  {
     ResetGlobalAnim_Clickable();
 
+    gfx.cursor_mode_override = CURSOR_UNDEFINED;
+  }
+
   DrawGlobalAnimationsExt(drawing_target, drawing_stage);
 
   if (drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
+  {
     ResetGlobalAnim_Clicked();
+  }
+
+  if (gfx.cursor_mode_override != last_cursor_mode_override)
+    SetMouseCursor(gfx.cursor_mode);
 }
 
 static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part)
@@ -848,12 +897,24 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
   }
   else if (part->control_info.class == get_hash_from_key("pointer"))
   {
-    viewport_x = gfx.mouse_x + part->control_info.x;
-    viewport_y = gfx.mouse_y + part->control_info.y;
+    int mx = MIN(MAX(0, gfx.mouse_x), WIN_XSIZE - 1);
+    int my = MIN(MAX(0, gfx.mouse_y), WIN_YSIZE - 1);
+
+    // prevent displaying off-screen custom mouse cursor in upper left corner
+    if (gfx.mouse_x == POS_OFFSCREEN &&
+       gfx.mouse_y == POS_OFFSCREEN)
+      mx = my = POS_OFFSCREEN;
+
+    viewport_x = mx - part->control_info.x;
+    viewport_y = my - part->control_info.y;
     viewport_width  = part->graphic_info.width;
     viewport_height = part->graphic_info.height;
 
     part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_2;
+
+    // do not use global animation mouse pointer when reloading artwork
+    if (global.anim_status != GAME_MODE_LOADING)
+      gfx.cursor_mode_override = CURSOR_NONE;
   }
   else if (part->control_info.class == get_hash_from_key("door_1"))
   {
@@ -897,7 +958,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
     part->viewport_width  = viewport_width;
     part->viewport_height = viewport_height;
 
-    changed = TRUE;
+    if (part->control_info.class != get_hash_from_key("pointer"))
+      changed = TRUE;
   }
 
   return changed;
@@ -921,7 +983,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
 }
@@ -936,8 +998,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
 }
 
@@ -957,8 +1019,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
 }
 
@@ -972,8 +1034,8 @@ static void StopGlobalAnimMusic(struct GlobalAnimPartControlInfo *part)
   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
 }
 
@@ -1086,13 +1148,18 @@ static boolean isClickedPart(struct GlobalAnimPartControlInfo *part,
   return TRUE;
 }
 
+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);
 }
 
 static void InitGlobalAnim_Triggered(struct GlobalAnimPartControlInfo *part,
-                                    boolean *anything_clicked,
+                                    boolean *click_consumed,
                                     boolean *any_event_action,
                                     int event_value, char *info_text)
 {
@@ -1116,22 +1183,24 @@ 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))
        continue;
 
       if (isClickablePart(part2, mask))
       {
        part2->triggered = TRUE;
-       *anything_clicked = clickConsumed(part);        // click was on "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
@@ -1142,37 +1211,53 @@ 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);
-
-      printf("\n");
+       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 HandleGlobalAnimDelay(struct GlobalAnimPartControlInfo *part,
+                                 int delay_type, char *info_text)
+{
+#if DEBUG_ANIM_DELAY
+  Debug("anim:HandleGlobalAnimDelay", "%d.%d %s",
+       part->old_anim_nr + 1, part->old_nr + 1, info_text);
+#endif
+
+  DoGlobalAnim_DelayAction(part, delay_type);
+}
+
 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 anything_clicked = FALSE;
+  boolean click_consumed = FALSE;
   boolean any_event_action = FALSE;
 
   // check if this event is defined to trigger other animations
-  InitGlobalAnim_Triggered(part, &anything_clicked, &any_event_action,
+  InitGlobalAnim_Triggered(part, &click_consumed, &any_event_action,
                           event_value, info_text);
 }
 
 static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
                                 int state)
 {
+  if (handle_click && !part->clicked)
+    return state;
+
   struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[part->mode_nr];
   struct GlobalAnimMainControlInfo *anim = &ctrl->anim[part->anim_nr];
   struct GraphicInfo *g = &part->graphic_info;
@@ -1205,6 +1290,10 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
     part->initial_anim_sync_frame =
       (g->anim_global_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->direction & MV_HORIZONTAL)
     {
       int pos_bottom = part->viewport_height - g->height;
@@ -1294,6 +1383,7 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
     {
       PlayGlobalAnimSoundAndMusic(part);
 
+      HandleGlobalAnimDelay(part, ANIM_DELAY_INIT,  "START [INIT_DELAY]");
       HandleGlobalAnimEvent(part, ANIM_EVENT_START, "START [ANIM]");
     }
     else
@@ -1333,6 +1423,7 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
 
       PlayGlobalAnimSoundAndMusic(part);
 
+      HandleGlobalAnimDelay(part, ANIM_DELAY_INIT,  "START [INIT_DELAY]");
       HandleGlobalAnimEvent(part, ANIM_EVENT_START, "START [ANIM]");
     }
 
@@ -1383,7 +1474,8 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
 
       StopGlobalAnimSoundAndMusic(part);
 
-      HandleGlobalAnimEvent(part, ANIM_EVENT_END, "END [ANIM_DELAY/EVENT]");
+      HandleGlobalAnimDelay(part, ANIM_DELAY_ANIM, "END [ANIM_DELAY]");
+      HandleGlobalAnimEvent(part, ANIM_EVENT_END,  "END [ANIM_DELAY/EVENT]");
 
       part->post_delay_counter =
        (c->post_delay_fixed + GetSimpleRandom(c->post_delay_random));
@@ -1402,6 +1494,7 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
 
     if (part->post_delay_counter == 0)
     {
+      HandleGlobalAnimDelay(part, ANIM_DELAY_POST, "END [POST_DELAY]");
       HandleGlobalAnimEvent(part, ANIM_EVENT_POST, "END [POST_DELAY]");
 
       return ANIM_STATE_RESTART;
@@ -1422,8 +1515,8 @@ 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;
   }
@@ -1448,21 +1541,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)
@@ -1498,8 +1592,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++)
@@ -1596,8 +1690,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++)
@@ -1607,7 +1700,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);
@@ -1618,7 +1711,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
@@ -1633,14 +1726,31 @@ static void DoAnimationExt(void)
 #endif
 }
 
+static void DoGlobalAnim_DelayAction(struct GlobalAnimPartControlInfo *part,
+                                    int delay_type)
+{
+  int delay_action =
+    (delay_type == ANIM_DELAY_INIT ? part->control_info.init_delay_action :
+     delay_type == ANIM_DELAY_ANIM ? part->control_info.anim_delay_action :
+     delay_type == ANIM_DELAY_POST ? part->control_info.post_delay_action :
+     ANIM_DELAY_ACTION_NONE);
+
+  if (delay_action == ANIM_DELAY_ACTION_NONE)
+    return;
+
+  PushUserEvent(USEREVENT_ANIM_DELAY_ACTION, delay_action, 0);
+}
+
 static boolean DoGlobalAnim_EventAction(struct GlobalAnimPartControlInfo *part)
 {
-  int anim_event_action = part->control_info.anim_event_action;
+  int event_action = (part->init_event_state ?
+                     part->control_info.init_event_action :
+                     part->control_info.anim_event_action);
 
-  if (anim_event_action == -1)
+  if (event_action == ANIM_EVENT_ACTION_NONE)
     return FALSE;
 
-  PushUserEvent(USEREVENT_ANIM_EVENT_ACTION, anim_event_action, 0);
+  PushUserEvent(USEREVENT_ANIM_EVENT_ACTION, event_action, 0);
 
   // check if further actions are allowed to be executed
   if (part->control_info.style & STYLE_MULTIPLE_ACTIONS)
@@ -1683,10 +1793,12 @@ static void InitGlobalAnim_Clickable(void)
 
 static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
 {
+  boolean click_consumed = FALSE;
   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--)
@@ -1715,7 +1827,7 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
        if (!part->clickable)
          continue;
 
-       if (part->state != ANIM_STATE_RUNNING)
+       if (!(part->state & ANIM_STATE_RUNNING))
          continue;
 
        // always handle "any" click events (clicking anywhere on screen) ...
@@ -1723,12 +1835,12 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
            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
 
-         part->clicked = TRUE;
-         anything_clicked = clickConsumed(part);
+         anything_clicked = part->clicked = TRUE;
+         click_consumed |= clickConsumed(part);
        }
 
        // always handle "unclick:any" events (releasing anywhere on screen) ...
@@ -1736,12 +1848,12 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
            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
 
-         part->clicked = TRUE;
-         anything_clicked = clickConsumed(part);
+         anything_clicked = part->clicked = TRUE;
+         click_consumed |= clickConsumed(part);
        }
 
        // ... but only handle the first (topmost) clickable animation
@@ -1752,8 +1864,8 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
            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
@@ -1761,28 +1873,44 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
            any_event_action = TRUE;
 
          // determine if mouse clicks should be blocked from other animations
-         any_part_clicked = clickConsumed(part);
+         any_part_clicked |= clickConsumed(part);
 
          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
 
-           part->clicked = TRUE;
-           anything_clicked = clickConsumed(part);
+           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, &anything_clicked, &any_event_action,
+         InitGlobalAnim_Triggered(part, &click_consumed, &any_event_action,
                                   ANIM_EVENT_CLICK, "CLICK");
        }
       }
     }
   }
 
-  return (anything_clicked || any_event_action);
+  if (anything_clicked)
+  {
+    handle_click = TRUE;
+
+    for (i = 0; i < NUM_GAME_MODES; i++)
+      HandleGlobalAnim(ANIM_CONTINUE, i);
+
+    handle_click = FALSE;
+
+    // prevent ignoring release event if processed within same game frame
+    StopProcessingEvents();
+  }
+
+  return (click_consumed || any_event_action);
 }
 
 static void ResetGlobalAnim_Clickable(void)