reverted order of processing game modes for global animations clicks
[rocksndiamonds.git] / src / anim.c
index 808f7c8b20b0d6da965a58e3b6723d69fde12bb4..dcd66e330f2862decf9856ccabd6c3ba97860a8b 100644 (file)
@@ -115,7 +115,7 @@ struct GlobalAnimPartControlInfo
   struct GraphicInfo graphic_info;
   struct GraphicInfo control_info;
 
-  boolean class_playfield;
+  boolean class_playfield_or_door;
 
   int viewport_x;
   int viewport_y;
@@ -137,6 +137,9 @@ struct GlobalAnimPartControlInfo
   int anim_delay_counter;
   int post_delay_counter;
 
+  int fade_delay_counter;
+  int fade_alpha;
+
   boolean init_event_state;
   boolean anim_event_state;
 
@@ -312,7 +315,7 @@ int getAnimationFrame(int num_frames, int delay, int mode, int start_frame,
   }
   else if (mode & ANIM_LEVEL_NR)       // play frames by level number
   {
-    int level_pos = level_nr - leveldir_current->first_level;
+    int level_pos = level_nr - gfx.anim_first_level;
 
     frame = level_pos % num_frames;
   }
@@ -357,10 +360,8 @@ static int compareGlobalAnimPartControlInfo(const void *obj1, const void *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
-    compare_result = o1->nr - o2->nr;
+  // do not sort animations parts by draw order (as it would be confusing)
+  compare_result = o1->nr - o2->nr;
 
   return compare_result;
 }
@@ -381,9 +382,9 @@ static int compareGlobalAnimMainControlInfo(const void *obj1, const void *obj2)
   return compare_result;
 }
 
-static boolean isPausedOnPlayfield(struct GlobalAnimPartControlInfo *part)
+static boolean isPausedOnPlayfieldOrDoor(struct GlobalAnimPartControlInfo *part)
 {
-  // only pause playfield animations when playing
+  // only pause playfield and door animations when playing
   if (game_status != GAME_MODE_PLAYING)
     return FALSE;
 
@@ -391,12 +392,12 @@ static boolean isPausedOnPlayfield(struct GlobalAnimPartControlInfo *part)
   if (checkGameEnded())
     return FALSE;
 
-  // only pause animations on playfield
-  if (!part->class_playfield)
+  // 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 open(ing)
-  if (!tape.pausing && !game.request_active_or_moving)
+  // only pause animations when engine is paused or request dialog is active
+  if (!tape.pausing && !game.request_active)
     return FALSE;
 
   return TRUE;
@@ -735,6 +736,7 @@ static void BlitGlobalAnimation(struct GlobalAnimPartControlInfo *part,
   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++)
@@ -781,6 +783,8 @@ static void BlitGlobalAnimation(struct GlobalAnimPartControlInfo *part,
       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);
@@ -951,8 +955,8 @@ static void DrawGlobalAnimationsExt(int drawing_target, int drawing_stage)
        if (part->drawing_stage != drawing_stage)
          continue;
 
-       // if game is paused, also pause playfield animations
-       if (isPausedOnPlayfield(part))
+       // if game is paused, also pause playfield and door animations
+       if (isPausedOnPlayfieldOrDoor(part))
          part->initial_anim_sync_frame++;
 
        sync_frame = anim_sync_frame - part->initial_anim_sync_frame;
@@ -1028,7 +1032,7 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
 
   part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_1;
 
-  part->class_playfield = FALSE;
+  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"))
@@ -1067,6 +1071,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"))
   {
@@ -1084,6 +1090,8 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
       viewport_width  = VXSIZE;
       viewport_height = VYSIZE;
     }
+
+    part->class_playfield_or_door = TRUE;
   }
   else         // default: "playfield"
   {
@@ -1092,7 +1100,7 @@ static boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part
     viewport_width  = FULL_SXSIZE;
     viewport_height = FULL_SYSIZE;
 
-    part->class_playfield = TRUE;
+    part->class_playfield_or_door = TRUE;
   }
 
   if (viewport_x != part->viewport_x ||
@@ -1267,8 +1275,7 @@ static boolean isClickablePart(struct GlobalAnimPartControlInfo *part, int mask)
        return TRUE;
     }
   }
-
-  if (part->anim_event_state)
+  else if (part->anim_event_state)
   {
     int num_anim_events = GetGlobalAnimEventValueCount(c->anim_event);
 
@@ -1344,6 +1351,28 @@ static boolean clickConsumed(struct GlobalAnimPartControlInfo *part)
   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,
                                     boolean *click_consumed,
                                     boolean *any_event_action,
@@ -1369,7 +1398,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))
@@ -1412,7 +1441,9 @@ static void InitGlobalAnim_Triggered(struct GlobalAnimPartControlInfo *part,
 }
 
 static void InitGlobalAnim_Triggered_ByCustomElement(int nr, int page,
-                                                    int x, int y)
+                                                    int x, int y,
+                                                    int trigger_x,
+                                                    int trigger_y)
 {
   struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[GAME_MODE_PLAYING];
 
@@ -1430,25 +1461,27 @@ static void InitGlobalAnim_Triggered_ByCustomElement(int nr, int page,
     {
       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) && !part2->triggered)
       {
        struct GraphicInfo *c = &part2->control_info;
 
-       if (c->position == POS_CE)
+       if (c->position == POS_CE ||
+           c->position == POS_CE_TRIGGER)
        {
-         // may contain negative tile offset that is treated as "off-screen"
-         part2->x = 0;
-         part2->y = 0;
-
          // store CE tile and offset position to handle scrolling
-         part2->tile_x = x;
-         part2->tile_y = y;
+         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;
        }
@@ -1506,9 +1539,10 @@ 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 animations
-  if (isPausedOnPlayfield(part))
+  // if game is paused, also pause playfield and door animations
+  if (isPausedOnPlayfieldOrDoor(part))
     return state;
 
   if (viewport_changed)
@@ -1542,6 +1576,18 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
     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)
     {
       int pos_bottom = part->viewport_height - g->height;
@@ -1755,6 +1801,14 @@ 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);
 
@@ -1773,26 +1827,10 @@ static int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part,
   }
 #endif
 
-  if (c->position == POS_CE)
+  if (c->position == POS_CE ||
+      c->position == POS_CE_TRIGGER)
   {
-    // 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;
+    SetGlobalAnimPartTileXY(part);
   }
   else
   {
@@ -1921,9 +1959,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)
@@ -2095,6 +2137,11 @@ static boolean InitGlobalAnim_Clicked(int mx, int my, int clicked_event)
       {
        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;
@@ -2254,12 +2301,13 @@ int getGlobalAnimSyncFrame(void)
   return anim_sync_frame;
 }
 
-void HandleGlobalAnimEventByElementChange(int element, int page, int x, int y)
+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);
+                                          x, y, trigger_x, trigger_y);
 }