changed "http" to "https" in URLs
[rocksndiamonds.git] / src / anim.c
index e603a9d38a0e3760e616a83f623db24c2aac39d1..dec9d18ee61d49cf1667e61298111e2c9f9060d0 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
 // ============================================================================
@@ -108,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;
@@ -242,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
@@ -409,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;
@@ -513,6 +518,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;
@@ -764,6 +770,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;
@@ -796,10 +803,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);
 
@@ -880,6 +898,11 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
     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;
@@ -1123,13 +1146,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)
 {
@@ -1153,13 +1181,13 @@ 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",
@@ -1209,11 +1237,11 @@ static void HandleGlobalAnimEvent(struct GlobalAnimPartControlInfo *part,
   printf("::: %d.%d %s\n", 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);
 }
 
@@ -1255,6 +1283,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;
@@ -1754,10 +1786,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--)
@@ -1786,7 +1820,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) ...
@@ -1798,8 +1832,8 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
                 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) ...
@@ -1811,8 +1845,8 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
                 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
@@ -1832,7 +1866,7 @@ 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))
          {
@@ -1841,12 +1875,15 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
                   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");
        }
       }
@@ -1857,12 +1894,16 @@ 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;
+
+    // prevent ignoring release event if processed within same game frame
+    StopProcessingEvents();
   }
 
-  return (anything_clicked || any_event_action);
+  return (click_consumed || any_event_action);
 }
 
 static void ResetGlobalAnim_Clickable(void)