added drawing global animations below and above masked screen border
[rocksndiamonds.git] / src / cartoons.c
index 2ef229f14f023df4ce0bc4d43e3ce49f154b6cc9..b2f2eb2b8f4ca5528a8d79f1a1b8b64f16c3a419 100644 (file)
-/***********************************************************
-*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
-*----------------------------------------------------------*
-*  (c) 1995-98 Artsoft Entertainment                       *
-*              Holger Schemel                              *
-*              Oststrasse 11a                              *
-*              33604 Bielefeld                             *
-*              phone: ++49 +521 290471                     *
-*              email: aeglos@valinor.owl.de                *
-*----------------------------------------------------------*
-*  cartoons.c                                              *
-***********************************************************/
+// ============================================================================
+// Rocks'n'Diamonds - McDuffin Strikes Back!
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// cartoons.c
+// ============================================================================
 
 #include "cartoons.h"
 #include "main.h"
 #include "tools.h"
 
 
-static void HandleAnimation(int);
-static boolean AnimateToon(int, boolean);
-static void DrawAnim(Bitmap, GC, int, int, int, int, int, int, int, int);
+/* values for global toon animation definition */
+#define NUM_GLOBAL_TOON_ANIMS          1
+#define NUM_GLOBAL_TOON_PARTS          MAX_NUM_TOONS
 
-struct AnimInfo
+/* values for global animation definition (including toons) */
+#define NUM_GLOBAL_ANIMS_AND_TOONS     (NUM_GLOBAL_ANIMS +             \
+                                        NUM_GLOBAL_TOON_ANIMS)
+#define NUM_GLOBAL_ANIM_PARTS_AND_TOONS        MAX(NUM_GLOBAL_ANIM_PARTS_ALL,  \
+                                           NUM_GLOBAL_TOON_PARTS)
+
+struct GlobalAnimPartControlInfo
 {
-  int width, height;
-  int src_x, src_y;
-  int frames;
-  int frames_per_second;
-  int stepsize;
-  boolean pingpong;
-  int direction;
-  int position;
+  int nr;
+  int anim_nr;
+  int mode_nr;
+
+  int graphic;
+  struct GraphicInfo graphic_info;
+  struct GraphicInfo control_info;
+
+  int viewport_x;
+  int viewport_y;
+  int viewport_width;
+  int viewport_height;
+
+  int x, y;
+  int step_xoffset, step_yoffset;
+
+  unsigned int initial_anim_sync_frame;
+  unsigned int step_delay, step_delay_value;
+
+  int init_delay_counter;
+  int anim_delay_counter;
+  int post_delay_counter;
+
+  int drawing_stage;
+
+  int state;
+  int last_game_status;
 };
 
-/* values for cartoon figures */
-#define NUM_TOONS      18
-
-#define DWARF_XSIZE    40
-#define DWARF_YSIZE    48
-#define DWARF_X                2
-#define DWARF_Y                72
-#define DWARF2_Y       186
-#define DWARF_FRAMES   8
-#define DWARF_FPS      10
-#define DWARF_STEPSIZE 4
-#define JUMPER_XSIZE   48
-#define JUMPER_YSIZE   56
-#define JUMPER_X       2
-#define JUMPER_Y       125
-#define JUMPER_FRAMES  8
-#define JUMPER_FPS     10
-#define JUMPER_STEPSIZE        4
-#define CLOWN_XSIZE    80
-#define CLOWN_YSIZE    110
-#define CLOWN_X                327
-#define CLOWN_Y                10
-#define CLOWN_FRAMES   1
-#define CLOWN_FPS      10
-#define CLOWN_STEPSIZE 4
-#define BIRD_XSIZE     32
-#define BIRD_YSIZE     30
-#define BIRD1_X                2
-#define BIRD1_Y                2
-#define BIRD2_X                2
-#define BIRD2_Y                37
-#define BIRD_FRAMES    8
-#define BIRD_FPS       20
-#define BIRD_STEPSIZE  4
-
-#define GAMETOON_XSIZE         TILEX
-#define GAMETOON_YSIZE         TILEY
-#define GAMETOON_FRAMES_4      4
-#define GAMETOON_FRAMES_8      8
-#define GAMETOON_FPS           20
-#define GAMETOON_STEPSIZE      4
-
-#define ANIMDIR_LEFT   1
-#define ANIMDIR_RIGHT  2
-#define ANIMDIR_UP     4
-#define ANIMDIR_DOWN   8
-
-#define ANIMPOS_ANY    0
-#define ANIMPOS_LEFT   1
-#define ANIMPOS_RIGHT  2
-#define ANIMPOS_UP     4
-#define ANIMPOS_DOWN   8
-#define ANIMPOS_UPPER  16
-
-#define ANIM_START     0
-#define ANIM_CONTINUE  1
-#define ANIM_STOP      2
+struct GlobalAnimMainControlInfo
+{
+  struct GlobalAnimPartControlInfo base;
+  struct GlobalAnimPartControlInfo part[NUM_GLOBAL_ANIM_PARTS_AND_TOONS];
 
-void InitAnimation()
+  int nr;
+  int mode_nr;
+
+  struct GraphicInfo control_info;
+
+  int num_parts;
+  int part_counter;
+  int active_part_nr;
+
+  boolean has_base;
+
+  int init_delay_counter;
+
+  int state;
+};
+
+struct GlobalAnimControlInfo
 {
-  HandleAnimation(ANIM_START);
+  struct GlobalAnimMainControlInfo anim[NUM_GLOBAL_ANIMS_AND_TOONS];
+
+  int nr;
+  int num_anims;
+};
+
+
+/* forward declaration for internal use */
+static void DoAnimationExt(void);
+
+static struct GlobalAnimControlInfo global_anim_ctrl[NUM_SPECIAL_GFX_ARGS];
+static struct ToonInfo toons[MAX_NUM_TOONS];
+
+static unsigned int anim_sync_frame = 0;
+static unsigned int anim_sync_frame_delay = 0;
+static unsigned int anim_sync_frame_delay_value = GAME_FRAME_DELAY;
+
+static boolean do_animations = FALSE;
+
+
+static int getGlobalAnimationPart(struct GlobalAnimMainControlInfo *anim)
+{
+  struct GraphicInfo *c = &anim->control_info;
+  int last_anim_random_frame = gfx.anim_random_frame;
+  int part_nr;
+
+  gfx.anim_random_frame = -1;  // (use simple, ad-hoc random numbers)
+
+  part_nr = getAnimationFrame(anim->num_parts, 1,
+                             c->anim_mode, c->anim_start_frame,
+                             anim->part_counter);
+
+  gfx.anim_random_frame = last_anim_random_frame;
+
+  return part_nr;
 }
 
-void StopAnimation()
+static void PrepareBackbuffer()
+{
+  if (game_status != GAME_MODE_PLAYING)
+    return;
+
+  BlitScreenToBitmap(backbuffer);
+}
+
+boolean ToonNeedsRedraw()
 {
-  HandleAnimation(ANIM_STOP);
+  return TRUE;
 }
 
-void DoAnimation()
+void InitToons()
 {
-  HandleAnimation(ANIM_CONTINUE);
+  int num_toons = MAX_NUM_TOONS;
+  int i;
+
+  if (global.num_toons >= 0 && global.num_toons < MAX_NUM_TOONS)
+    num_toons = global.num_toons;
+
+  for (i = 0; i < num_toons; i++)
+  {
+    int graphic = IMG_TOON_1 + i;
+    struct FileInfo *image = getImageListEntryFromImageID(graphic);
+
+    toons[i].bitmap = graphic_info[graphic].bitmap;
+
+    toons[i].src_x = graphic_info[graphic].src_x;
+    toons[i].src_y = graphic_info[graphic].src_y;
+
+    toons[i].width  = graphic_info[graphic].width;
+    toons[i].height = graphic_info[graphic].height;
+
+    toons[i].anim_frames      = graphic_info[graphic].anim_frames;
+    toons[i].anim_delay       = graphic_info[graphic].anim_delay;
+    toons[i].anim_mode        = graphic_info[graphic].anim_mode;
+    toons[i].anim_start_frame = graphic_info[graphic].anim_start_frame;
+
+    toons[i].step_offset = graphic_info[graphic].step_offset;
+    toons[i].step_delay  = graphic_info[graphic].step_delay;
+
+    toons[i].direction = image->parameter[GFX_ARG_DIRECTION];
+    toons[i].position = image->parameter[GFX_ARG_POSITION];
+  }
+
+  InitToonScreen(bitmap_db_toons,
+                BackToFront, PrepareBackbuffer, ToonNeedsRedraw,
+                toons, num_toons,
+                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+                GAME_FRAME_DELAY);
 }
 
-void HandleAnimation(int mode)
+static void InitToonControls()
 {
-  static unsigned long animstart_delay = -1;
-  static unsigned long animstart_delay_value = 0;
-  static boolean anim_restart = TRUE;
-  static boolean reset_delay = TRUE;
-  static int toon_nr = 0;
-  int draw_mode;
-
-  if (!setup.toons)
-    return;
+  struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[GAME_MODE_DEFAULT];
+  struct GlobalAnimMainControlInfo *anim = &ctrl->anim[ctrl->num_anims];
+  int mode_nr, anim_nr, part_nr;
+  int control = IMG_INTERNAL_GLOBAL_TOON_DEFAULT;
+  int num_toons = MAX_NUM_TOONS;
+  int i;
+
+  if (global.num_toons >= 0 && global.num_toons < MAX_NUM_TOONS)
+    num_toons = global.num_toons;
 
-  switch(mode)
+  mode_nr = GAME_MODE_DEFAULT;
+  anim_nr = ctrl->num_anims;
+
+  anim->nr = anim_nr;
+  anim->mode_nr = mode_nr;
+  anim->control_info = graphic_info[control];
+
+  anim->num_parts = 0;
+  anim->part_counter = 0;
+  anim->active_part_nr = 0;
+
+  anim->has_base = FALSE;
+
+  anim->init_delay_counter = 0;
+
+  anim->state = ANIM_STATE_INACTIVE;
+
+  part_nr = 0;
+
+  for (i = 0; i < num_toons; i++)
   {
-    case ANIM_START:
-      anim_restart = TRUE;
-      reset_delay = TRUE;
+    struct GlobalAnimPartControlInfo *part = &anim->part[part_nr];
+    int graphic = IMG_TOON_1 + i;
+    int control = graphic;
+
+    part->nr = part_nr;
+    part->anim_nr = anim_nr;
+    part->mode_nr = mode_nr;
+    part->graphic = graphic;
+    part->graphic_info = graphic_info[graphic];
+    part->control_info = graphic_info[control];
+
+    part->graphic_info.anim_delay *= part->graphic_info.step_delay;
+
+    part->control_info.init_delay_fixed = 0;
+    part->control_info.init_delay_random = 150;
+
+    part->control_info.x = ARG_UNDEFINED_VALUE;
+    part->control_info.y = ARG_UNDEFINED_VALUE;
+
+    part->initial_anim_sync_frame = 0;
+
+    part->step_delay = 0;
+    part->step_delay_value = graphic_info[control].step_delay;
+
+    part->state = ANIM_STATE_INACTIVE;
+    part->last_game_status = -1;
+
+    anim->num_parts++;
+    part_nr++;
+  }
 
-      /* Fill empty backbuffer for animation functions */
-      if (setup.direct_draw && game_status == PLAYING)
+  ctrl->num_anims++;
+}
+
+void InitGlobalAnimControls()
+{
+  int m, a, p;
+  int mode_nr, anim_nr, part_nr;
+  int graphic, control;
+
+  anim_sync_frame = 0;
+
+  ResetDelayCounter(&anim_sync_frame_delay);
+
+  for (m = 0; m < NUM_SPECIAL_GFX_ARGS; m++)
+  {
+    mode_nr = m;
+
+    struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr];
+
+    ctrl->nr = mode_nr;
+    ctrl->num_anims = 0;
+
+    anim_nr = 0;
+
+    for (a = 0; a < NUM_GLOBAL_ANIMS; a++)
+    {
+      struct GlobalAnimMainControlInfo *anim = &ctrl->anim[anim_nr];
+      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;
+
+      anim->nr = anim_nr;
+      anim->mode_nr = mode_nr;
+      anim->control_info = graphic_info[control];
+
+      anim->num_parts = 0;
+      anim->part_counter = 0;
+      anim->active_part_nr = 0;
+
+      anim->has_base = FALSE;
+
+      anim->init_delay_counter = 0;
+
+      anim->state = ANIM_STATE_INACTIVE;
+
+      part_nr = 0;
+
+      for (p = 0; p < NUM_GLOBAL_ANIM_PARTS_ALL; p++)
       {
-       int xx,yy;
+       struct GlobalAnimPartControlInfo *part = &anim->part[part_nr];
 
-       SetDrawtoField(DRAW_BACKBUFFER);
+       graphic = global_anim_info[a].graphic[p][m];
+       control = global_anim_info[ctrl_id].graphic[p][m];
 
-       for(xx=0; xx<SCR_FIELDX; xx++)
-         for(yy=0; yy<SCR_FIELDY; yy++)
-           DrawScreenField(xx,yy);
-       DrawAllPlayers();
+       if (graphic == IMG_UNDEFINED || graphic_info[graphic].bitmap == NULL ||
+           control == IMG_UNDEFINED)
+         continue;
 
-       SetDrawtoField(DRAW_DIRECT);
+#if 0
+       printf("::: mode == %d, anim = %d, part = %d [%d, %d, %d] [%d]\n",
+              m, a, p, mode_nr, anim_nr, part_nr, control);
+#endif
+
+       part->nr = part_nr;
+       part->anim_nr = anim_nr;
+       part->mode_nr = mode_nr;
+       part->graphic = graphic;
+       part->graphic_info = graphic_info[graphic];
+       part->control_info = graphic_info[control];
+
+       part->initial_anim_sync_frame = 0;
+
+       part->step_delay = 0;
+       part->step_delay_value = graphic_info[control].step_delay;
+
+       part->state = ANIM_STATE_INACTIVE;
+       part->last_game_status = -1;
+
+       if (p < GLOBAL_ANIM_ID_PART_BASE)
+       {
+         anim->num_parts++;
+         part_nr++;
+       }
+       else
+       {
+         anim->base = *part;
+         anim->has_base = TRUE;
+       }
       }
 
-      if (setup.soft_scrolling && game_status == PLAYING)
+      if (anim->num_parts > 0 || anim->has_base)
       {
-       int fx = FX, fy = FY;
+       ctrl->num_anims++;
+       anim_nr++;
+      }
+    }
+  }
 
-        fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
-        fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
+  InitToonControls();
+}
 
-       BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
-      }
+void DrawGlobalAnimExt(int drawing_stage)
+{
+  int mode_nr;
 
-      return;
-      break;
-    case ANIM_CONTINUE:
-      break;
-    case ANIM_STOP:
-      redraw_mask |= (REDRAW_FIELD | REDRAW_FROM_BACKBUFFER);
+  if (game_status == GAME_MODE_LOADING)
+    do_animations = FALSE;
+
+  if (!do_animations || !setup.toons)
+    return;
 
-      /* Redraw background even when in direct drawing mode */
-      draw_mode = setup.direct_draw;
-      setup.direct_draw = FALSE;
+  if (drawing_stage == DRAW_GLOBAL_ANIM_STAGE_1)
+    DoAnimationExt();
 
-      BackToFront();
+  for (mode_nr = 0; mode_nr < NUM_SPECIAL_GFX_ARGS; mode_nr++)
+  {
+    struct GlobalAnimControlInfo *ctrl = &global_anim_ctrl[mode_nr];
+    int anim_nr;
 
-      setup.direct_draw = draw_mode;
+    if (mode_nr != GFX_SPECIAL_ARG_DEFAULT &&
+       mode_nr != game_status)
+      continue;
 
-      return;
-      break;
-    default:
-      break;
+    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;
+
+       if (!(part->state & ANIM_STATE_RUNNING))
+         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 (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));
+
+       dst_x += part->viewport_x;
+       dst_y += part->viewport_y;
+
+       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);
+
+       getFixedGraphicSource(part->graphic, frame, &src_bitmap,
+                             &src_x, &src_y);
+
+       src_x += cut_x;
+       src_y += cut_y;
+
+       BlitToScreenMasked(src_bitmap, src_x, src_y, width, height,
+                          dst_x, dst_y);
+      }
+    }
   }
+}
 
-  if (reset_delay)
+void DrawGlobalAnim(int drawing_stage)
+{
+  if (!do_animations || !setup.toons)
+    return;
+
+  DrawGlobalAnimExt(drawing_stage);
+}
+
+boolean SetGlobalAnimPart_Viewport(struct GlobalAnimPartControlInfo *part)
+{
+  int viewport_x;
+  int viewport_y;
+  int viewport_width;
+  int viewport_height;
+  boolean changed = FALSE;
+
+  if (part->last_game_status == game_status)
+    return FALSE;
+
+  part->last_game_status = game_status;
+
+  part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_1;
+
+  if (part->control_info.class == get_hash_from_key("window") ||
+      part->control_info.class == get_hash_from_key("border"))
+  {
+    viewport_x = 0;
+    viewport_y = 0;
+    viewport_width  = WIN_XSIZE;
+    viewport_height = WIN_YSIZE;
+
+    part->drawing_stage = DRAW_GLOBAL_ANIM_STAGE_2;
+  }
+  else if (part->control_info.class == get_hash_from_key("door_1"))
+  {
+    viewport_x = DX;
+    viewport_y = DY;
+    viewport_width  = DXSIZE;
+    viewport_height = DYSIZE;
+  }
+  else if (part->control_info.class == get_hash_from_key("door_2"))
+  {
+    viewport_x = VX;
+    viewport_y = VY;
+    viewport_width  = VXSIZE;
+    viewport_height = VYSIZE;
+  }
+  else         // default: "playfield"
   {
-    animstart_delay = Counter();
-    animstart_delay_value = SimpleRND(3000);
-    reset_delay = FALSE;
+    viewport_x = REAL_SX;
+    viewport_y = REAL_SY;
+    viewport_width  = FULL_SXSIZE;
+    viewport_height = FULL_SYSIZE;
   }
 
-  if (anim_restart)
+  if (viewport_x != part->viewport_x ||
+      viewport_y != part->viewport_y ||
+      viewport_width  != part->viewport_width ||
+      viewport_height != part->viewport_height)
   {
-    if (!DelayReached(&animstart_delay, animstart_delay_value))
-      return;
+    part->viewport_x = viewport_x;
+    part->viewport_y = viewport_y;
+    part->viewport_width  = viewport_width;
+    part->viewport_height = viewport_height;
 
-    toon_nr = SimpleRND(NUM_TOONS);
+    changed = TRUE;
   }
 
-  anim_restart = reset_delay = AnimateToon(toon_nr,anim_restart);
+  return changed;
 }
 
-boolean AnimateToon(int toon_nr, boolean restart)
+int HandleGlobalAnim_Part(struct GlobalAnimPartControlInfo *part, int state)
 {
-  static int pos_x = 0, pos_y = 0;
-  static int delta_x = 0, delta_y = 0;
-  static int frame = 0, frame_step = 1;
-  static boolean horiz_move, vert_move;
-  static unsigned long anim_delay = 0;
-  static unsigned long anim_delay_value = 0;
-  static int width,height;
-  static int pad_x,pad_y;
-  static int cut_x,cut_y;
-  static int src_x, src_y;
-  static int dest_x, dest_y;
-  static struct AnimInfo toon[NUM_TOONS] =
-  {
-    {
-      DWARF_XSIZE, DWARF_YSIZE,
-      DWARF_X, DWARF_Y,
-      DWARF_FRAMES,
-      DWARF_FPS,
-      DWARF_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      DWARF_XSIZE, DWARF_YSIZE,
-      DWARF_X, DWARF2_Y,
-      DWARF_FRAMES,
-      DWARF_FPS,
-      DWARF_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      JUMPER_XSIZE, JUMPER_YSIZE,
-      JUMPER_X, JUMPER_Y,
-      JUMPER_FRAMES,
-      JUMPER_FPS,
-      JUMPER_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      CLOWN_XSIZE, CLOWN_YSIZE,
-      CLOWN_X, CLOWN_Y,
-      CLOWN_FRAMES,
-      CLOWN_FPS,
-      CLOWN_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_UP,
-      ANIMPOS_ANY
-    },
-    {
-      BIRD_XSIZE, BIRD_YSIZE,
-      BIRD1_X, BIRD1_Y,
-      BIRD_FRAMES,
-      BIRD_FPS,
-      BIRD_STEPSIZE,
-      ANIM_OSCILLATE,
-      ANIMDIR_RIGHT,
-      ANIMPOS_UPPER
-    },
-    {
-      BIRD_XSIZE, BIRD_YSIZE,
-      BIRD2_X, BIRD2_Y,
-      BIRD_FRAMES,
-      BIRD_FPS,
-      BIRD_STEPSIZE,
-      ANIM_OSCILLATE,
-      ANIMDIR_LEFT,
-      ANIMPOS_UPPER
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SPIELER1_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SPIELER1_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SPIELER1_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SPIELER1_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_PINGUIN_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_PINGUIN_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_PINGUIN_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_PINGUIN_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_MOLE_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_MOLE_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_MOLE_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_MOLE_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SCHWEIN_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SCHWEIN_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SCHWEIN_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SCHWEIN_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_DRACHE_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_DRACHE_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_DRACHE_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_DRACHE_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SONDE - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SONDE - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_8,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_ANY
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SONDE - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SONDE - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_8,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_ANY
-    },
-  };
-  struct AnimInfo *anim = &toon[toon_nr];
-  int anim_bitmap_nr = (toon_nr < 6 ? PIX_TOONS : PIX_HEROES);
-  Bitmap anim_bitmap = pix[anim_bitmap_nr];
-  GC anim_clip_gc = pix[anim_bitmap_nr]->stored_clip_gc;
-
-  if (restart)
+  struct GraphicInfo *g = &part->graphic_info;
+  struct GraphicInfo *c = &part->control_info;
+  boolean viewport_changed = SetGlobalAnimPart_Viewport(part);
+
+  if (viewport_changed)
+    state |= ANIM_STATE_RESTART;
+
+  if (state & ANIM_STATE_RESTART)
   {
-    horiz_move = (anim->direction & (ANIMDIR_LEFT | ANIMDIR_RIGHT));
-    vert_move = (anim->direction & (ANIMDIR_UP | ANIMDIR_DOWN));
-    anim_delay_value = 1000/anim->frames_per_second;
-    frame = 0;
+    ResetDelayCounterExt(&part->step_delay, anim_sync_frame);
+
+    part->init_delay_counter =
+      (c->init_delay_fixed + GetSimpleRandom(c->init_delay_random));
 
-    if (horiz_move)
+    part->anim_delay_counter =
+      (c->anim_delay_fixed + GetSimpleRandom(c->anim_delay_random));
+
+    part->initial_anim_sync_frame =
+      (g->anim_global_sync ? 0 : anim_sync_frame + part->init_delay_counter);
+
+    if (c->direction & MV_HORIZONTAL)
     {
-      if (anim->position==ANIMPOS_UP)
-       pos_y = 0;
-      else if (anim->position==ANIMPOS_DOWN)
-       pos_y = FULL_SYSIZE-anim->height;
-      else if (anim->position==ANIMPOS_UPPER)
-       pos_y = SimpleRND((FULL_SYSIZE-anim->height)/2);
+      int pos_bottom = part->viewport_height - g->height;
+
+      if (c->position == POS_TOP)
+       part->y = 0;
+      else if (c->position == POS_UPPER)
+       part->y = GetSimpleRandom(pos_bottom / 2);
+      else if (c->position == POS_MIDDLE)
+       part->y = pos_bottom / 2;
+      else if (c->position == POS_LOWER)
+       part->y = pos_bottom / 2 + GetSimpleRandom(pos_bottom / 2);
+      else if (c->position == POS_BOTTOM)
+       part->y = pos_bottom;
       else
-       pos_y = SimpleRND(FULL_SYSIZE-anim->height);
+       part->y = GetSimpleRandom(pos_bottom);
 
-      if (anim->direction==ANIMDIR_RIGHT)
+      if (c->direction == MV_RIGHT)
       {
-       delta_x = anim->stepsize;
-       pos_x = -anim->width+delta_x;
+       part->step_xoffset = c->step_offset;
+       part->x = -g->width + part->step_xoffset;
       }
       else
       {
-       delta_x = -anim->stepsize;
-       pos_x = FULL_SXSIZE+delta_x;
+       part->step_xoffset = -c->step_offset;
+       part->x = part->viewport_width + part->step_xoffset;
       }
-      delta_y = 0;
+
+      part->step_yoffset = 0;
     }
-    else
+    else if (c->direction & MV_VERTICAL)
     {
-      if (anim->position==ANIMPOS_LEFT)
-       pos_x = 0;
-      else if (anim->position==ANIMPOS_RIGHT)
-       pos_x = FULL_SXSIZE-anim->width;
+      int pos_right = part->viewport_width - g->width;
+
+      if (c->position == POS_LEFT)
+       part->x = 0;
+      else if (c->position == POS_RIGHT)
+       part->x = pos_right;
       else
-       pos_x = SimpleRND(FULL_SXSIZE-anim->width);
+       part->x = GetSimpleRandom(pos_right);
 
-      if (anim->direction==ANIMDIR_DOWN)
+      if (c->direction == MV_DOWN)
       {
-       delta_y = anim->stepsize;
-       pos_y = -anim->height+delta_y;
+       part->step_yoffset = c->step_offset;
+       part->y = -g->height + part->step_yoffset;
       }
       else
       {
-       delta_y = -anim->stepsize;
-       pos_y = FULL_SYSIZE+delta_y;
+       part->step_yoffset = -c->step_offset;
+       part->y = part->viewport_height + part->step_yoffset;
       }
-      delta_x = 0;
+
+      part->step_xoffset = 0;
+    }
+    else
+    {
+      part->x = 0;
+      part->y = 0;
+
+      part->step_xoffset = 0;
+      part->step_yoffset = 0;
     }
+
+    if (c->x != ARG_UNDEFINED_VALUE)
+      part->x = c->x;
+    if (c->y != ARG_UNDEFINED_VALUE)
+      part->y = c->y;
+
+    if (c->step_xoffset != ARG_UNDEFINED_VALUE)
+      part->step_xoffset = c->step_xoffset;
+    if (c->step_yoffset != ARG_UNDEFINED_VALUE)
+      part->step_yoffset = c->step_yoffset;
   }
 
-  if (pos_x <= -anim->width  - anim->stepsize ||
-      pos_x >=  FULL_SXSIZE  + anim->stepsize ||
-      pos_y <= -anim->height - anim->stepsize ||
-      pos_y >=  FULL_SYSIZE  + anim->stepsize)
-    return(TRUE);
+  if (part->init_delay_counter > 0)
+  {
+    part->init_delay_counter--;
+
+    return ANIM_STATE_WAITING;
+  }
 
-  if (!DelayReached(&anim_delay, anim_delay_value))
+  if ((part->x <= -g->width              && part->step_xoffset <= 0) ||
+      (part->x >=  part->viewport_width  && part->step_xoffset >= 0) ||
+      (part->y <= -g->height             && part->step_yoffset <= 0) ||
+      (part->y >=  part->viewport_height && part->step_yoffset >= 0))
+    return ANIM_STATE_RESTART;
+
+  if (part->anim_delay_counter > 0)
   {
-    if ((game_status == HELPSCREEN ||
-        (game_status == MAINMENU && redraw_mask & REDRAW_MICROLEVEL))
-       && !restart)
-      DrawAnim(anim_bitmap, anim_clip_gc,
-              src_x + cut_x, src_y + cut_y, width, height,
-              REAL_SX + dest_x, REAL_SY + dest_y, pad_x, pad_y);
-
-    return(FALSE);
+    part->anim_delay_counter--;
+
+    if (part->anim_delay_counter == 0)
+    {
+      part->post_delay_counter =
+       (c->post_delay_fixed + GetSimpleRandom(c->post_delay_random));
+
+      if (part->post_delay_counter > 0)
+       return ANIM_STATE_RUNNING;
+
+      return ANIM_STATE_RESTART | ANIM_STATE_RUNNING;
+    }
   }
 
-  if (pos_x<-anim->width)
-    pos_x = -anim->width;
-  else if (pos_x>FULL_SXSIZE)
-    pos_x = FULL_SXSIZE;
-  if (pos_y<-anim->height)
-    pos_y = -anim->height;
-  else if (pos_y>FULL_SYSIZE)
-    pos_y = FULL_SYSIZE;
-
-  pad_x = (horiz_move ? anim->stepsize : 0);
-  pad_y = (vert_move  ? anim->stepsize : 0);
-  src_x = anim->src_x + frame * anim->width;
-  src_y = anim->src_y;
-  dest_x = pos_x;
-  dest_y = pos_y;
-  cut_x = cut_y = 0;
-  width  = anim->width;
-  height = anim->height;
-
-  if (pos_x<0)
+  if (part->post_delay_counter > 0)
   {
-    dest_x = 0;
-    width += pos_x;
-    cut_x = -pos_x;
+    part->post_delay_counter--;
+
+    if (part->post_delay_counter == 0)
+      return ANIM_STATE_RESTART;
+
+    return ANIM_STATE_WAITING;
   }
-  else if (pos_x>FULL_SXSIZE-anim->width)
-    width -= (pos_x - (FULL_SXSIZE-anim->width));
 
-  if (pos_y<0)
+  if (!DelayReachedExt(&part->step_delay, part->step_delay_value,
+                      anim_sync_frame))
+    return ANIM_STATE_RUNNING;
+
+#if 0
   {
-    dest_y = 0;
-    height += pos_y;
-    cut_y = -pos_y;
+    static unsigned int last_counter = -1;
+    unsigned int counter = Counter();
+
+    printf("::: NEXT ANIM PART [%d, %d]\n",
+          anim_sync_frame, counter - last_counter);
+
+    last_counter = counter;
   }
-  else if (pos_y>FULL_SYSIZE-anim->height)
-    height -= (pos_y - (FULL_SYSIZE-anim->height));
+#endif
+
+  part->x += part->step_xoffset;
+  part->y += part->step_yoffset;
+
+  return ANIM_STATE_RUNNING;
+}
 
-  DrawAnim(anim_bitmap,anim_clip_gc,
-          src_x+cut_x,src_y+cut_y, width,height,
-          REAL_SX+dest_x,REAL_SY+dest_y, pad_x,pad_y);
+void HandleGlobalAnim_Main(struct GlobalAnimMainControlInfo *anim, int action)
+{
+  struct GlobalAnimPartControlInfo *part;
+  struct GraphicInfo *c = &anim->control_info;
+  boolean skip = FALSE;
+
+#if 0
+  printf("::: HandleGlobalAnim_Main: %d, %d => %d\n",
+        anim->mode_nr, anim->nr, anim->num_parts);
+  printf("::: %d, %d, %d\n", global.num_toons, setup.toons, num_toons);
+#endif
 
-  pos_x += delta_x;
-  pos_y += delta_y;
-  frame += frame_step;
+#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);
+#endif
 
-  if (frame<0 || frame>=anim->frames)
+  switch (action)
   {
-    if (anim->pingpong)
+    case ANIM_START:
+      anim->state = ANIM_STATE_RESTART;
+      anim->part_counter = 0;
+      anim->active_part_nr = 0;
+      skip = TRUE;
+
+      break;
+
+    case ANIM_CONTINUE:
+      if (anim->state == ANIM_STATE_INACTIVE)
+       skip = TRUE;
+
+      break;
+
+    case ANIM_STOP:
+      anim->state = ANIM_STATE_INACTIVE;
+      skip = TRUE;
+
+      break;
+
+    default:
+      break;
+  }
+
+  if (c->anim_mode & ANIM_ALL || anim->num_parts == 0)
+  {
+    int num_parts = anim->num_parts + (anim->has_base ? 1 : 0);
+    int i;
+
+#if 0
+    printf("::: HandleGlobalAnim_Main: %d, %d => %d\n",
+          anim->mode_nr, anim->nr, num_parts);
+#endif
+
+    for (i = 0; i < num_parts; i++)
     {
-      frame_step *= -1;
-      frame = (frame<0 ? 1 : anim->frames-2);
+      part = &anim->part[i];
+
+      switch (action)
+      {
+        case ANIM_START:
+         anim->state = ANIM_STATE_RUNNING;
+         part->state = ANIM_STATE_RESTART;
+         skip = TRUE;
+
+         break;
+
+        case ANIM_CONTINUE:
+         if (part->state == ANIM_STATE_INACTIVE)
+           skip = TRUE;
+
+         break;
+
+        case ANIM_STOP:
+         part->state = ANIM_STATE_INACTIVE;
+         skip = TRUE;
+
+         break;
+
+        default:
+         break;
+      }
+
+      if (skip)
+       continue;
+
+      part->state = HandleGlobalAnim_Part(part, part->state);
     }
-    else
-      frame = (frame<0 ? anim->frames-1 : 0);
+
+    return;
   }
 
-  return(FALSE);
+  if (skip)
+    return;
+
+  if (anim->state & ANIM_STATE_RESTART)                // directly after restart
+    anim->active_part_nr = getGlobalAnimationPart(anim);
+
+  part = &anim->part[anim->active_part_nr];
+
+  part->state = ANIM_STATE_RUNNING;
+
+  anim->state = HandleGlobalAnim_Part(part, anim->state);
+
+  if (anim->state & ANIM_STATE_RESTART)
+    anim->part_counter++;
+}
+
+void HandleGlobalAnim_Mode(struct GlobalAnimControlInfo *ctrl, int action)
+{
+  int i;
+
+#if 0
+  printf("::: HandleGlobalAnim_Mode: %d => %d\n",
+        ctrl->nr, ctrl->num_anims);
+#endif
+
+  for (i = 0; i < ctrl->num_anims; i++)
+    HandleGlobalAnim_Main(&ctrl->anim[i], action);
 }
 
-void DrawAnim(Bitmap toon_bitmap, GC toon_clip_gc,
-             int src_x, int src_y, int width, int height,
-             int dest_x, int dest_y, int pad_x, int pad_y)
+void HandleGlobalAnim(int action)
 {
-  int buf_x = DOOR_GFX_PAGEX3, buf_y = DOOR_GFX_PAGEY1;
+#if 0
+  printf("::: HandleGlobalAnim [mode == %d]\n", game_status);
+#endif
+
+  HandleGlobalAnim_Mode(&global_anim_ctrl[GAME_MODE_DEFAULT], action);
+  HandleGlobalAnim_Mode(&global_anim_ctrl[game_status], action);
+}
+
+void InitAnimation()
+{
+  // HandleAnimation(ANIM_START);
+
+#if 0
+  printf("::: InitAnimation\n");
+#endif
+
+  // InitCounter();
+
+  InitGlobalAnimControls();
+
+  HandleGlobalAnim(ANIM_START);
+
+  do_animations = TRUE;
+}
+
+void StopAnimation()
+{
+  // HandleAnimation(ANIM_STOP);
+
+#if 0
+  printf("::: StopAnimation\n");
+#endif
+
+  HandleGlobalAnim(ANIM_STOP);
+
+  do_animations = FALSE;
+}
+
+static void DoAnimationExt()
+{
+#if 0
+  printf("::: DoAnimation [%d, %d]\n", anim_sync_frame, Counter());
+#endif
 
 #if 1
-  /* special method to avoid flickering interference with BackToFront() */
-  BlitBitmap(backbuffer, pix[PIX_DB_DOOR], dest_x-pad_x, dest_y-pad_y,
-            width+2*pad_x, height+2*pad_y, buf_x, buf_y);
-  SetClipOrigin(toon_bitmap, toon_clip_gc, dest_x-src_x, dest_y-src_y);
-  BlitBitmapMasked(toon_bitmap, backbuffer,
-                  src_x, src_y, width, height, dest_x, dest_y);
-  BlitBitmap(backbuffer, window, dest_x-pad_x, dest_y-pad_y,
-            width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
-  BackToFront();
-  BlitBitmap(pix[PIX_DB_DOOR], backbuffer, buf_x, buf_y,
-           width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
+  WaitUntilDelayReached(&anim_sync_frame_delay, anim_sync_frame_delay_value);
+  anim_sync_frame++;
 #else
-  /* normal method, causing flickering interference with BackToFront() */
-  BlitBitmap(backbuffer, pix[PIX_DB_DOOR], dest_x-pad_x, dest_y-pad_y,
-            width+2*pad_x, height+2*pad_y, buf_x, buf_y);
-  SetClipOrigin(toon_bitmap,toon_clip_gc, buf_x-src_x+pad_x,buf_y-src_y+pad_y);
-  BlitBitmapMasked(toon_bitmap, pix[PIX_DB_DOOR],
-                  src_x, src_y, width, height, buf_x+pad_x, buf_y+pad_y);
-  BlitBitmap(pix[PIX_DB_DOOR], window, buf_x, buf_y,
-            width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
+  if (DelayReached(&anim_sync_frame_delay, anim_sync_frame_delay_value))
+    anim_sync_frame++;
+#endif
+
+  HandleGlobalAnim(ANIM_CONTINUE);
+
+#if 1
+  // force screen redraw in next frame to continue drawing global animations
+  redraw_mask = REDRAW_ALL;
 #endif
+}
+
+void DoAnimation()
+{
+  // HandleAnimation(ANIM_CONTINUE);
 
-  FlushDisplay();
+#if 1
+  // force screen redraw in next frame to continue drawing global animations
+  redraw_mask = REDRAW_ALL;
+#endif
 }