rnd-20100202-1-src
[rocksndiamonds.git] / src / tools.c
index 23be2acbf3a7e03f1171bbbe36bcbb9254d65a81..aacba67058f44f8bebadf1863a52c87cb4bd551e 100644 (file)
@@ -144,7 +144,17 @@ void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
     /* blit playfield from scroll buffer to normal back buffer for fading in */
     BlitScreenToBitmap_EM(backbuffer);
   }
-  else if (game_status == GAME_MODE_PLAYING && !game.envelope_active)
+  else if (game_status == GAME_MODE_PLAYING &&
+          level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    /* currently there is no partial redraw -- always redraw whole playfield */
+    RedrawPlayfield_SP(TRUE);
+
+    /* blit playfield from scroll buffer to normal back buffer for fading in */
+    BlitScreenToBitmap_SP(backbuffer);
+  }
+  else if (game_status == GAME_MODE_PLAYING &&
+          !game.envelope_active)
   {
     if (force_redraw)
     {
@@ -257,9 +267,19 @@ void DrawMaskedBorder(int redraw_mask)
 
 void BackToFront()
 {
-  int x,y;
+  int x, y;
   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
 
+#if 0
+  printf("::: TILES TO REFRESH: %d\n", redraw_tiles);
+  for (x = 0; x < SCR_FIELDX; x++)
+    for (y = 0 ; y < SCR_FIELDY; y++)
+      if (redraw[redraw_x1 + x][redraw_y1 + y])
+       printf("::: - %d, %d [%s]\n",
+              LEVELX(x), LEVELY(y),
+              EL_NAME(Feld[LEVELX(x)][LEVELY(y)]));
+#endif
+
   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
     redraw_mask |= REDRAW_FIELD;
 
@@ -427,8 +447,12 @@ void BackToFront()
     if (!global.fps_slowdown)
       info1[0] = '\0';
 
-    sprintf(text, "%.1f fps%s", global.frames_per_second, info1);
+    sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
+#if 1
+    DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
+#else
     DrawTextExt(window, SX, SY, text, FONT_TEXT_2, BLIT_OPAQUE);
+#endif
   }
 
   FlushDisplay();
@@ -489,6 +513,16 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
     return;
   }
 
+#if 0
+  printf("::: !!! FADING %d ... [%d] [%d]\n", fade_mode, fade_type,
+        fade_type_skip);
+#endif
+
+#if 1
+  fade_delay = fading.fade_delay;
+  post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
+#endif
+
   if (fade_type_skip != FADE_TYPE_NONE)
   {
 #if 0
@@ -499,7 +533,11 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
     if (fade_type & fade_type_skip)
       fade_type_skip = FADE_TYPE_NONE;
 
+#if 1
+    fade_delay = 0;
+#else
     return;
+#endif
   }
 
 #if 1
@@ -520,7 +558,7 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
   }
 #endif
 
-  /* !!! what abount fade_mask == REDRAW_FIELD | REDRAW_ALL ??? !!! */
+  /* !!! what about fade_mask == REDRAW_FIELD | REDRAW_ALL ??? !!! */
 
 #if 0
   printf("::: NOW FADING %d ... [%d]\n", fade_mode, fade_type);
@@ -539,8 +577,10 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
     width  = FULL_SXSIZE;
     height = FULL_SYSIZE;
 
+#if 0
     fade_delay = fading.fade_delay;
     post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
+#endif
 
     if (border.draw_masked_when_fading)
       draw_border_function = DrawMaskedBorder_FIELD;   /* update when fading */
@@ -554,8 +594,10 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
     width  = WIN_XSIZE;
     height = WIN_YSIZE;
 
+#if 0
     fade_delay = fading.fade_delay;
     post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
+#endif
   }
 
 #if 1
@@ -1200,14 +1242,43 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
   int y1 = y;
   int x2 = x + SIGN(dx);
   int y2 = y + SIGN(dy);
+#if 0
+  /* !!! DOES NOT WORK FOR SLOW MOVEMENT !!! */
+  int sync_frame = GfxFrame[LEVELX(x)][LEVELY(y)];
+#else
+  /* movement with two-tile animations must be sync'ed with movement position,
+     not with current GfxFrame (which can be higher when using slow movement) */
+  int anim_pos = (dx ? ABS(dx) : ABS(dy));
   int anim_frames = graphic_info[graphic].anim_frames;
-  int sync_frame = (dx ? ABS(dx) : ABS(dy)) * anim_frames / TILESIZE;
+#if 1
+  /* (we also need anim_delay here for movement animations with less frames) */
+  int anim_delay = graphic_info[graphic].anim_delay;
+  int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
+#else
+  int sync_frame = anim_pos * anim_frames / TILESIZE;
+#endif
+#endif
   boolean draw_start_tile = (cut_mode != CUT_ABOVE);   /* only for falling! */
   boolean draw_end_tile   = (cut_mode != CUT_BELOW);   /* only for falling! */
 
   /* re-calculate animation frame for two-tile movement animation */
   frame = getGraphicAnimationFrame(graphic, sync_frame);
 
+#if 0
+#if 0
+  printf("::: %d, %d, %d => %d [%d]\n",
+        anim_pos, anim_frames, anim_delay, sync_frame, graphic);
+#else
+  printf("::: %d, %d => %d\n",
+        anim_pos, anim_frames, sync_frame);
+#endif
+#endif
+
+#if 0
+  printf("::: %d [%d, %d] [%d] [%d]\n", frame, sync_frame, dy,
+        GfxFrame[LEVELX(x)][LEVELY(y)], mask_mode);
+#endif
+
   /* check if movement start graphic inside screen area and should be drawn */
   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
   {
@@ -1364,6 +1435,13 @@ void DrawLevelFieldThruMask(int x, int y)
   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
 }
 
+/* !!! implementation of quicksand is totally broken !!! */
+#define IS_CRUMBLED_TILE(x, y, e)                                      \
+       (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
+                            !IS_MOVING(x, y) ||                        \
+                            (e) == EL_QUICKSAND_EMPTYING ||            \
+                            (e) == EL_QUICKSAND_FAST_EMPTYING))
+
 static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
 {
   Bitmap *src_bitmap;
@@ -1386,7 +1464,11 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
   element = TILE_GFX_ELEMENT(x, y);
 
   /* crumble field itself */
+#if 1
+  if (IS_CRUMBLED_TILE(x, y, element))
+#else
   if (GFX_CRUMBLED(element) && !IS_MOVING(x, y))
+#endif
   {
     if (!IN_SCR_FIELD(sx, sy))
       return;
@@ -1402,8 +1484,13 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
                 BorderElement);
 
       /* check if neighbour field is of same type */
+#if 1
+      if (IS_CRUMBLED_TILE(xx, yy, element))
+       continue;
+#else
       if (GFX_CRUMBLED(element) && !IS_MOVING(xx, yy))
        continue;
+#endif
 
       if (i == 1 || i == 2)
       {
@@ -1435,18 +1522,29 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
       int sxx = sx + xy[i][0];
       int syy = sy + xy[i][1];
 
+#if 1
+      if (!IN_LEV_FIELD(xx, yy) ||
+         !IN_SCR_FIELD(sxx, syy))
+       continue;
+#else
       if (!IN_LEV_FIELD(xx, yy) ||
          !IN_SCR_FIELD(sxx, syy) ||
          IS_MOVING(xx, yy))
        continue;
+#endif
 
       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
        continue;
 
       element = TILE_GFX_ELEMENT(xx, yy);
 
+#if 1
+      if (!IS_CRUMBLED_TILE(xx, yy, element))
+       continue;
+#else
       if (!GFX_CRUMBLED(element))
        continue;
+#endif
 
       graphic = el_act2crm(element, ACTION_DEFAULT);
       crumbled_border_size = graphic_info[graphic].border_size;
@@ -1600,6 +1698,7 @@ void DrawScreenField(int x, int y)
       element = getBorderElement(lx, ly);
 
     DrawScreenElement(x, y, element);
+
     return;
   }
 
@@ -1625,8 +1724,22 @@ void DrawScreenField(int x, int y)
             element == EL_DC_MAGIC_WALL_FILLING)
       cut_mode = CUT_BELOW;
 
+#if 0
+    if (lx == 9 && ly == 1)
+      printf("::: %s [%d] [%d, %d] [%d]\n",
+            EL_NAME(TILE_GFX_ELEMENT(lx, ly)),
+            el_act2crm(TILE_GFX_ELEMENT(lx, ly), ACTION_DEFAULT),
+            element_info[EL_QUICKSAND_EMPTYING].graphic[ACTION_DEFAULT],
+            element_info[EL_QUICKSAND_EMPTYING].crumbled[ACTION_DEFAULT],
+            GFX_CRUMBLED(TILE_GFX_ELEMENT(lx, ly)));
+#endif
+
     if (cut_mode == CUT_ABOVE)
+#if 1
+      DrawScreenElement(x, y, element);
+#else
       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
+#endif
     else
       DrawScreenElement(x, y, EL_EMPTY);
 
@@ -1635,8 +1748,16 @@ void DrawScreenField(int x, int y)
     else if (cut_mode == NO_CUTTING)
       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
     else
+    {
       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
 
+#if 1
+      if (cut_mode == CUT_BELOW &&
+         IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
+       DrawLevelElement(lx, ly + 1, element);
+#endif
+    }
+
     if (content == EL_ACID)
     {
       int dir = MovDir[lx][ly];
@@ -1891,7 +2012,13 @@ void DrawLevel()
 {
   int x,y;
 
+#if 1
+  SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
+  SetDrawBackgroundMask(REDRAW_FIELD);
+#else
   SetDrawBackgroundMask(REDRAW_NONE);
+#endif
+
   ClearField();
 
   for (x = BX1; x <= BX2; x++)
@@ -2363,6 +2490,8 @@ void DrawPlayerField(int x, int y)
   DrawPlayer(PLAYERINFO(x, y));
 }
 
+#define DRAW_PLAYER_OVER_PUSHED_ELEMENT        1
+
 void DrawPlayer(struct PlayerInfo *player)
 {
   int jx = player->jx;
@@ -2502,6 +2631,7 @@ void DrawPlayer(struct PlayerInfo *player)
     }
   }
 
+#if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
   /* ----------------------------------------------------------------------- */
   /* draw player himself                                                     */
   /* ----------------------------------------------------------------------- */
@@ -2539,6 +2669,17 @@ void DrawPlayer(struct PlayerInfo *player)
 
     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
   }
+#endif
+
+#if DRAW_PLAYER_OVER_PUSHED_ELEMENT
+  if (player->GfxPos)
+  {
+    if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
+      sxx = player->GfxPos;
+    else
+      syy = player->GfxPos;
+  }
+#endif
 
   /* ----------------------------------------------------------------------- */
   /* draw things the player is pushing, if needed                            */
@@ -2578,11 +2719,87 @@ void DrawPlayer(struct PlayerInfo *player)
 #endif
 
     /* draw background element under pushed element (like the Sokoban field) */
+#if 1
+    /* this allows transparent pushing animation over non-black background */
+    if (IS_MOVING(jx, jy))
+    {
+      if (Back[jx][jy])
+       DrawLevelElement(jx, jy, Back[jx][jy]);
+      else
+       DrawLevelElement(jx, jy, EL_EMPTY);
+
+      if (Back[next_jx][next_jy])
+       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
+      else
+       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
+    }
+#else
     if (Back[next_jx][next_jy])
       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
+#endif
+
+#if 0
+    printf("::: %d, %d, %d, %d [%d] [%d, %d, %d] [%d] [%d, %d] [%d, %d]\n",
+          jx, px, player->GfxPos, player->StepFrame,
+          player->is_pushing,
+          dx, sxx, pxx,
+          IS_MOVING(jx, jy),
+          graphic, frame,
+          GfxFrame[jx][jy], GfxFrame[next_jx][next_jy]);
+#endif
 
+#if 1
+    /* do not draw (EM style) pushing animation when pushing is finished */
+    /* (two-tile animations usually do not contain start and end frame) */
+    if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
+      DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
+    else
+      DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
+#else
     /* masked drawing is needed for EMC style (double) movement graphics */
+    /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
+#endif
+  }
+#endif
+
+#if DRAW_PLAYER_OVER_PUSHED_ELEMENT
+  /* ----------------------------------------------------------------------- */
+  /* draw player himself                                                     */
+  /* ----------------------------------------------------------------------- */
+
+  graphic = getPlayerGraphic(player, move_dir);
+
+  /* in the case of changed player action or direction, prevent the current
+     animation frame from being restarted for identical animations */
+  if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
+    player->Frame = last_player_frame;
+
+  frame = getGraphicAnimationFrame(graphic, player->Frame);
+
+  if (player->GfxPos)
+  {
+    if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
+      sxx = player->GfxPos;
+    else
+      syy = player->GfxPos;
+  }
+
+  if (!setup.soft_scrolling && ScreenMovPos)
+    sxx = syy = 0;
+
+  if (player_is_opaque)
+    DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
+  else
+    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
+
+  if (SHIELD_ON(player))
+  {
+    int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
+                  IMG_SHIELD_NORMAL_ACTIVE);
+    int frame = getGraphicAnimationFrame(graphic, -1);
+
+    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
   }
 #endif
 
@@ -2723,9 +2940,13 @@ boolean Request(char *text, unsigned int req_state)
     }
   }
 
-  if (game_status == GAME_MODE_PLAYING &&
-      level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    BlitScreenToBitmap_EM(backbuffer);
+  if (game_status == GAME_MODE_PLAYING)
+  {
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      BlitScreenToBitmap_EM(backbuffer);
+    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+      BlitScreenToBitmap_SP(backbuffer);
+  }
 
   /* disable deactivated drawing when quick-loading level tape recording */
   if (tape.playing && tape.deactivate_display)
@@ -3148,6 +3369,11 @@ unsigned int MoveDoor(unsigned int door_state)
     door_state &= ~DOOR_CLOSE_ALL;
   }
 
+#if 1
+  if (game_status == GAME_MODE_EDITOR)
+    door_state |= DOOR_NO_DELAY;
+#endif
+
   if (door_state & DOOR_ACTION)
   {
     boolean handle_door_1 = (door_state & DOOR_ACTION_1);
@@ -5951,6 +6177,8 @@ unsigned int InitRND(long seed)
 {
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
     return InitEngineRandom_EM(seed);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    return InitEngineRandom_SP(seed);
   else
     return InitEngineRandom_RND(seed);
 }
@@ -6128,6 +6356,19 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
                             action == ACTION_FILLING ||
                             action == ACTION_EMPTYING);
 
+  /* special case: graphic uses "2nd movement tile" and has defined
+     7 frames for movement animation (or less) => use default graphic
+     for last (8th) frame which ends the movement animation */
+  if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
+  {
+    action = ACTION_DEFAULT;   /* (keep action_* unchanged for now) */
+    graphic = (direction == MV_NONE ?
+              el_act2img(effective_element, action) :
+              el_act_dir2img(effective_element, action, direction));
+
+    g = &graphic_info[graphic];
+  }
+
 #if 0
   if (tile == Xsand_stonesand_1 ||
       tile == Xsand_stonesand_2 ||
@@ -6162,6 +6403,16 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
 
       GfxFrame[x][y]++;
 
+#if 1
+      /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
+      if (g->double_movement && frame_em == 0)
+      {
+       GfxFrame[x][y] = 0;
+
+       // printf("::: resetting... [%d]\n", tile);
+      }
+#endif
+
       if (move_dir == MV_LEFT)
        GfxFrame[x - 1][y] = GfxFrame[x][y];
       else if (move_dir == MV_RIGHT)
@@ -6216,13 +6467,21 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
 {
   int action = object_mapping[tile].action;
   int direction = object_mapping[tile].direction;
+  boolean is_backside = object_mapping[tile].is_backside;
   int effective_element = get_effective_element_EM(tile, frame_em);
+#if 1
+  int effective_action = action;
+#else
+  int effective_action = (frame_em < 7 ? action : ACTION_DEFAULT);
+#endif
   int graphic = (direction == MV_NONE ?
-                el_act2img(effective_element, action) :
-                el_act_dir2img(effective_element, action, direction));
+                el_act2img(effective_element, effective_action) :
+                el_act_dir2img(effective_element, effective_action,
+                               direction));
   int crumbled = (direction == MV_NONE ?
-                 el_act2crm(effective_element, action) :
-                 el_act_dir2crm(effective_element, action, direction));
+                 el_act2crm(effective_element, effective_action) :
+                 el_act_dir2crm(effective_element, effective_action,
+                                direction));
   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
@@ -6232,6 +6491,30 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
 #endif
   int sync_frame;
 
+  /* special case: graphic uses "2nd movement tile" and has defined
+     7 frames for movement animation (or less) => use default graphic
+     for last (8th) frame which ends the movement animation */
+  if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
+  {
+    effective_action = ACTION_DEFAULT;
+    graphic = (direction == MV_NONE ?
+              el_act2img(effective_element, effective_action) :
+              el_act_dir2img(effective_element, effective_action,
+                             direction));
+    crumbled = (direction == MV_NONE ?
+               el_act2crm(effective_element, effective_action) :
+               el_act_dir2crm(effective_element, effective_action,
+                              direction));
+
+    g = &graphic_info[graphic];
+  }
+
+#if 0
+  if (frame_em == 7)
+    return;
+#endif
+
+
 #if 0
   if (frame_em == 0)   /* reset animation frame for certain elements */
   {
@@ -6249,43 +6532,500 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
 
   SetRandomAnimationValue(x, y);
 
-  int frame = getAnimationFrame(g->anim_frames,
-                               g->anim_delay,
-                               g->anim_mode,
-                               g->anim_start_frame,
-                               sync_frame);
-
-  getGraphicSourceExt(graphic, frame, &g_em->bitmap,
-                     &g_em->src_x, &g_em->src_y, FALSE);
-
-  /* (updating the "crumbled" graphic definitions is probably not really needed,
-     as animations for crumbled graphics can't be longer than one EMC cycle) */
-#if 1
-  set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
-                          sync_frame);
-
-#else
-
-  g_em->crumbled_bitmap = NULL;
-  g_em->crumbled_src_x = 0;
-  g_em->crumbled_src_y = 0;
-
-  g_em->has_crumbled_graphics = FALSE;
-
-  if (has_crumbled_graphics && crumbled != IMG_EMPTY_SPACE)
-  {
-    int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
-                                          g_crumbled->anim_delay,
-                                          g_crumbled->anim_mode,
-                                          g_crumbled->anim_start_frame,
-                                          sync_frame);
-
-    getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
-                    &g_em->crumbled_src_x, &g_em->crumbled_src_y);
-
-    g_em->has_crumbled_graphics = TRUE;
-  }
-#endif
+#if 0
+  int i = tile;
+  int j = frame_em;
+  int xxx_sync_frame = (i == Xdrip_stretch ? 7 :
+                       i == Xdrip_stretchB ? 7 :
+                       i == Ydrip_s2 ? j + 8 :
+                       i == Ydrip_s2B ? j + 8 :
+                       i == Xacid_1 ? 0 :
+                       i == Xacid_2 ? 10 :
+                       i == Xacid_3 ? 20 :
+                       i == Xacid_4 ? 30 :
+                       i == Xacid_5 ? 40 :
+                       i == Xacid_6 ? 50 :
+                       i == Xacid_7 ? 60 :
+                       i == Xacid_8 ? 70 :
+                       i == Xfake_acid_1 ? 0 :
+                       i == Xfake_acid_2 ? 10 :
+                       i == Xfake_acid_3 ? 20 :
+                       i == Xfake_acid_4 ? 30 :
+                       i == Xfake_acid_5 ? 40 :
+                       i == Xfake_acid_6 ? 50 :
+                       i == Xfake_acid_7 ? 60 :
+                       i == Xfake_acid_8 ? 70 :
+                       i == Xball_2 ? 7 :
+                       i == Xball_2B ? j + 8 :
+                       i == Yball_eat ? j + 1 :
+                       i == Ykey_1_eat ? j + 1 :
+                       i == Ykey_2_eat ? j + 1 :
+                       i == Ykey_3_eat ? j + 1 :
+                       i == Ykey_4_eat ? j + 1 :
+                       i == Ykey_5_eat ? j + 1 :
+                       i == Ykey_6_eat ? j + 1 :
+                       i == Ykey_7_eat ? j + 1 :
+                       i == Ykey_8_eat ? j + 1 :
+                       i == Ylenses_eat ? j + 1 :
+                       i == Ymagnify_eat ? j + 1 :
+                       i == Ygrass_eat ? j + 1 :
+                       i == Ydirt_eat ? j + 1 :
+                       i == Xamoeba_1 ? 0 :
+                       i == Xamoeba_2 ? 1 :
+                       i == Xamoeba_3 ? 2 :
+                       i == Xamoeba_4 ? 3 :
+                       i == Xamoeba_5 ? 0 :
+                       i == Xamoeba_6 ? 1 :
+                       i == Xamoeba_7 ? 2 :
+                       i == Xamoeba_8 ? 3 :
+                       i == Xexit_2 ? j + 8 :
+                       i == Xexit_3 ? j + 16 :
+                       i == Xdynamite_1 ? 0 :
+                       i == Xdynamite_2 ? 8 :
+                       i == Xdynamite_3 ? 16 :
+                       i == Xdynamite_4 ? 24 :
+                       i == Xsand_stonein_1 ? j + 1 :
+                       i == Xsand_stonein_2 ? j + 9 :
+                       i == Xsand_stonein_3 ? j + 17 :
+                       i == Xsand_stonein_4 ? j + 25 :
+                       i == Xsand_stoneout_1 && j == 0 ? 0 :
+                       i == Xsand_stoneout_1 && j == 1 ? 0 :
+                       i == Xsand_stoneout_1 && j == 2 ? 1 :
+                       i == Xsand_stoneout_1 && j == 3 ? 2 :
+                       i == Xsand_stoneout_1 && j == 4 ? 2 :
+                       i == Xsand_stoneout_1 && j == 5 ? 3 :
+                       i == Xsand_stoneout_1 && j == 6 ? 4 :
+                       i == Xsand_stoneout_1 && j == 7 ? 4 :
+                       i == Xsand_stoneout_2 && j == 0 ? 5 :
+                       i == Xsand_stoneout_2 && j == 1 ? 6 :
+                       i == Xsand_stoneout_2 && j == 2 ? 7 :
+                       i == Xsand_stoneout_2 && j == 3 ? 8 :
+                       i == Xsand_stoneout_2 && j == 4 ? 9 :
+                       i == Xsand_stoneout_2 && j == 5 ? 11 :
+                       i == Xsand_stoneout_2 && j == 6 ? 13 :
+                       i == Xsand_stoneout_2 && j == 7 ? 15 :
+                       i == Xboom_bug && j == 1 ? 2 :
+                       i == Xboom_bug && j == 2 ? 2 :
+                       i == Xboom_bug && j == 3 ? 4 :
+                       i == Xboom_bug && j == 4 ? 4 :
+                       i == Xboom_bug && j == 5 ? 2 :
+                       i == Xboom_bug && j == 6 ? 2 :
+                       i == Xboom_bug && j == 7 ? 0 :
+                       i == Xboom_bomb && j == 1 ? 2 :
+                       i == Xboom_bomb && j == 2 ? 2 :
+                       i == Xboom_bomb && j == 3 ? 4 :
+                       i == Xboom_bomb && j == 4 ? 4 :
+                       i == Xboom_bomb && j == 5 ? 2 :
+                       i == Xboom_bomb && j == 6 ? 2 :
+                       i == Xboom_bomb && j == 7 ? 0 :
+                       i == Xboom_android && j == 7 ? 6 :
+                       i == Xboom_1 && j == 1 ? 2 :
+                       i == Xboom_1 && j == 2 ? 2 :
+                       i == Xboom_1 && j == 3 ? 4 :
+                       i == Xboom_1 && j == 4 ? 4 :
+                       i == Xboom_1 && j == 5 ? 6 :
+                       i == Xboom_1 && j == 6 ? 6 :
+                       i == Xboom_1 && j == 7 ? 8 :
+                       i == Xboom_2 && j == 0 ? 8 :
+                       i == Xboom_2 && j == 1 ? 8 :
+                       i == Xboom_2 && j == 2 ? 10 :
+                       i == Xboom_2 && j == 3 ? 10 :
+                       i == Xboom_2 && j == 4 ? 10 :
+                       i == Xboom_2 && j == 5 ? 12 :
+                       i == Xboom_2 && j == 6 ? 12 :
+                       i == Xboom_2 && j == 7 ? 12 :
+#if 0
+                       special_animation && j == 4 ? 3 :
+                       effective_action != action ? 0 :
+#endif
+                       j);
+#endif
+
+#if 0
+  int xxx_effective_action;
+  int xxx_has_action_graphics;
+
+  {
+    int element = object_mapping[i].element_rnd;
+    int action = object_mapping[i].action;
+    int direction = object_mapping[i].direction;
+    boolean is_backside = object_mapping[i].is_backside;
+#if 0
+    boolean action_removing = (action == ACTION_DIGGING ||
+                              action == ACTION_SNAPPING ||
+                              action == ACTION_COLLECTING);
+#endif
+    boolean action_exploding = ((action == ACTION_EXPLODING ||
+                                action == ACTION_SMASHED_BY_ROCK ||
+                                action == ACTION_SMASHED_BY_SPRING) &&
+                               element != EL_DIAMOND);
+    boolean action_active = (action == ACTION_ACTIVE);
+    boolean action_other = (action == ACTION_OTHER);
+
+    {
+#if 1
+      int effective_element = get_effective_element_EM(i, j);
+#else
+      int effective_element = (j > 5 && i == Yacid_splash_eB ? EL_EMPTY :
+                              j > 5 && i == Yacid_splash_wB ? EL_EMPTY :
+                              j < 7 ? element :
+                              i == Xdrip_stretch ? element :
+                              i == Xdrip_stretchB ? element :
+                              i == Ydrip_s1 ? element :
+                              i == Ydrip_s1B ? element :
+                              i == Xball_1B ? element :
+                              i == Xball_2 ? element :
+                              i == Xball_2B ? element :
+                              i == Yball_eat ? element :
+                              i == Ykey_1_eat ? element :
+                              i == Ykey_2_eat ? element :
+                              i == Ykey_3_eat ? element :
+                              i == Ykey_4_eat ? element :
+                              i == Ykey_5_eat ? element :
+                              i == Ykey_6_eat ? element :
+                              i == Ykey_7_eat ? element :
+                              i == Ykey_8_eat ? element :
+                              i == Ylenses_eat ? element :
+                              i == Ymagnify_eat ? element :
+                              i == Ygrass_eat ? element :
+                              i == Ydirt_eat ? element :
+                              i == Yemerald_stone ? EL_EMERALD :
+                              i == Ydiamond_stone ? EL_ROCK :
+                              i == Xsand_stonein_1 ? element :
+                              i == Xsand_stonein_2 ? element :
+                              i == Xsand_stonein_3 ? element :
+                              i == Xsand_stonein_4 ? element :
+                              is_backside ? EL_EMPTY :
+                              action_removing ? EL_EMPTY :
+                              element);
+#endif
+      int effective_action = (j < 7 ? action :
+                             i == Xdrip_stretch ? action :
+                             i == Xdrip_stretchB ? action :
+                             i == Ydrip_s1 ? action :
+                             i == Ydrip_s1B ? action :
+                             i == Xball_1B ? action :
+                             i == Xball_2 ? action :
+                             i == Xball_2B ? action :
+                             i == Yball_eat ? action :
+                             i == Ykey_1_eat ? action :
+                             i == Ykey_2_eat ? action :
+                             i == Ykey_3_eat ? action :
+                             i == Ykey_4_eat ? action :
+                             i == Ykey_5_eat ? action :
+                             i == Ykey_6_eat ? action :
+                             i == Ykey_7_eat ? action :
+                             i == Ykey_8_eat ? action :
+                             i == Ylenses_eat ? action :
+                             i == Ymagnify_eat ? action :
+                             i == Ygrass_eat ? action :
+                             i == Ydirt_eat ? action :
+                             i == Xsand_stonein_1 ? action :
+                             i == Xsand_stonein_2 ? action :
+                             i == Xsand_stonein_3 ? action :
+                             i == Xsand_stonein_4 ? action :
+                             i == Xsand_stoneout_1 ? action :
+                             i == Xsand_stoneout_2 ? action :
+                             i == Xboom_android ? ACTION_EXPLODING :
+                             action_exploding ? ACTION_EXPLODING :
+                             action_active ? action :
+                             action_other ? action :
+                             ACTION_DEFAULT);
+      int graphic = (el_act_dir2img(effective_element, effective_action,
+                                   direction));
+      int crumbled = (el_act_dir2crm(effective_element, effective_action,
+                                    direction));
+      int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
+      int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
+      boolean has_action_graphics = (graphic != base_graphic);
+      boolean has_crumbled_graphics = (base_crumbled != base_graphic);
+      struct GraphicInfo *g = &graphic_info[graphic];
+#if 0
+      struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
+#endif
+      struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
+      Bitmap *src_bitmap;
+      int src_x, src_y;
+      /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
+      boolean special_animation = (action != ACTION_DEFAULT &&
+                                  g->anim_frames == 3 &&
+                                  g->anim_delay == 2 &&
+                                  g->anim_mode & ANIM_LINEAR);
+      xxx_sync_frame = (i == Xdrip_stretch ? 7 :
+                       i == Xdrip_stretchB ? 7 :
+                       i == Ydrip_s2 ? j + 8 :
+                       i == Ydrip_s2B ? j + 8 :
+                       i == Xacid_1 ? 0 :
+                       i == Xacid_2 ? 10 :
+                       i == Xacid_3 ? 20 :
+                       i == Xacid_4 ? 30 :
+                       i == Xacid_5 ? 40 :
+                       i == Xacid_6 ? 50 :
+                       i == Xacid_7 ? 60 :
+                       i == Xacid_8 ? 70 :
+                       i == Xfake_acid_1 ? 0 :
+                       i == Xfake_acid_2 ? 10 :
+                       i == Xfake_acid_3 ? 20 :
+                       i == Xfake_acid_4 ? 30 :
+                       i == Xfake_acid_5 ? 40 :
+                       i == Xfake_acid_6 ? 50 :
+                       i == Xfake_acid_7 ? 60 :
+                       i == Xfake_acid_8 ? 70 :
+                       i == Xball_2 ? 7 :
+                       i == Xball_2B ? j + 8 :
+                       i == Yball_eat ? j + 1 :
+                       i == Ykey_1_eat ? j + 1 :
+                       i == Ykey_2_eat ? j + 1 :
+                       i == Ykey_3_eat ? j + 1 :
+                       i == Ykey_4_eat ? j + 1 :
+                       i == Ykey_5_eat ? j + 1 :
+                       i == Ykey_6_eat ? j + 1 :
+                       i == Ykey_7_eat ? j + 1 :
+                       i == Ykey_8_eat ? j + 1 :
+                       i == Ylenses_eat ? j + 1 :
+                       i == Ymagnify_eat ? j + 1 :
+                       i == Ygrass_eat ? j + 1 :
+                       i == Ydirt_eat ? j + 1 :
+                       i == Xamoeba_1 ? 0 :
+                       i == Xamoeba_2 ? 1 :
+                       i == Xamoeba_3 ? 2 :
+                       i == Xamoeba_4 ? 3 :
+                       i == Xamoeba_5 ? 0 :
+                       i == Xamoeba_6 ? 1 :
+                       i == Xamoeba_7 ? 2 :
+                       i == Xamoeba_8 ? 3 :
+                       i == Xexit_2 ? j + 8 :
+                       i == Xexit_3 ? j + 16 :
+                       i == Xdynamite_1 ? 0 :
+                       i == Xdynamite_2 ? 8 :
+                       i == Xdynamite_3 ? 16 :
+                       i == Xdynamite_4 ? 24 :
+                       i == Xsand_stonein_1 ? j + 1 :
+                       i == Xsand_stonein_2 ? j + 9 :
+                       i == Xsand_stonein_3 ? j + 17 :
+                       i == Xsand_stonein_4 ? j + 25 :
+                       i == Xsand_stoneout_1 && j == 0 ? 0 :
+                       i == Xsand_stoneout_1 && j == 1 ? 0 :
+                       i == Xsand_stoneout_1 && j == 2 ? 1 :
+                       i == Xsand_stoneout_1 && j == 3 ? 2 :
+                       i == Xsand_stoneout_1 && j == 4 ? 2 :
+                       i == Xsand_stoneout_1 && j == 5 ? 3 :
+                       i == Xsand_stoneout_1 && j == 6 ? 4 :
+                       i == Xsand_stoneout_1 && j == 7 ? 4 :
+                       i == Xsand_stoneout_2 && j == 0 ? 5 :
+                       i == Xsand_stoneout_2 && j == 1 ? 6 :
+                       i == Xsand_stoneout_2 && j == 2 ? 7 :
+                       i == Xsand_stoneout_2 && j == 3 ? 8 :
+                       i == Xsand_stoneout_2 && j == 4 ? 9 :
+                       i == Xsand_stoneout_2 && j == 5 ? 11 :
+                       i == Xsand_stoneout_2 && j == 6 ? 13 :
+                       i == Xsand_stoneout_2 && j == 7 ? 15 :
+                       i == Xboom_bug && j == 1 ? 2 :
+                       i == Xboom_bug && j == 2 ? 2 :
+                       i == Xboom_bug && j == 3 ? 4 :
+                       i == Xboom_bug && j == 4 ? 4 :
+                       i == Xboom_bug && j == 5 ? 2 :
+                       i == Xboom_bug && j == 6 ? 2 :
+                       i == Xboom_bug && j == 7 ? 0 :
+                       i == Xboom_bomb && j == 1 ? 2 :
+                       i == Xboom_bomb && j == 2 ? 2 :
+                       i == Xboom_bomb && j == 3 ? 4 :
+                       i == Xboom_bomb && j == 4 ? 4 :
+                       i == Xboom_bomb && j == 5 ? 2 :
+                       i == Xboom_bomb && j == 6 ? 2 :
+                       i == Xboom_bomb && j == 7 ? 0 :
+                       i == Xboom_android && j == 7 ? 6 :
+                       i == Xboom_1 && j == 1 ? 2 :
+                       i == Xboom_1 && j == 2 ? 2 :
+                       i == Xboom_1 && j == 3 ? 4 :
+                       i == Xboom_1 && j == 4 ? 4 :
+                       i == Xboom_1 && j == 5 ? 6 :
+                       i == Xboom_1 && j == 6 ? 6 :
+                       i == Xboom_1 && j == 7 ? 8 :
+                       i == Xboom_2 && j == 0 ? 8 :
+                       i == Xboom_2 && j == 1 ? 8 :
+                       i == Xboom_2 && j == 2 ? 10 :
+                       i == Xboom_2 && j == 3 ? 10 :
+                       i == Xboom_2 && j == 4 ? 10 :
+                       i == Xboom_2 && j == 5 ? 12 :
+                       i == Xboom_2 && j == 6 ? 12 :
+                       i == Xboom_2 && j == 7 ? 12 :
+                       special_animation && j == 4 ? 3 :
+                       effective_action != action ? 0 :
+                       j);
+
+      xxx_effective_action = effective_action;
+      xxx_has_action_graphics = has_action_graphics;
+    }
+  }
+#endif
+
+  int frame = getAnimationFrame(g->anim_frames,
+                               g->anim_delay,
+                               g->anim_mode,
+                               g->anim_start_frame,
+                               sync_frame);
+
+
+#if 0
+  return;
+#endif
+
+#if 0
+  if (frame_em == 7)
+    return;
+#endif
+
+#if 0
+  int old_src_x = g_em->src_x;
+  int old_src_y = g_em->src_y;
+#endif
+
+#if 1
+  getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
+                     g->double_movement && is_backside);
+#else
+  getGraphicSourceExt(graphic, frame, &g_em->bitmap,
+                     &g_em->src_x, &g_em->src_y, FALSE);
+#endif
+
+
+#if 0
+  return;
+#endif
+
+#if 0
+  if (frame_em == 7)
+  {
+    if (graphic == IMG_BUG_MOVING_RIGHT)
+      printf("::: %d, %d, %d: %d, %d [%d, %d -> %d, %d]\n", graphic, x, y,
+            g->double_movement, is_backside,
+            old_src_x, old_src_y, g_em->src_x, g_em->src_y);
+
+    return;
+  }
+#endif
+
+
+#if 0
+  g_em->src_offset_x = 0;
+  g_em->src_offset_y = 0;
+  g_em->dst_offset_x = 0;
+  g_em->dst_offset_y = 0;
+  g_em->width  = TILEX;
+  g_em->height = TILEY;
+
+  g_em->preserve_background = FALSE;
+#endif
+
+  /* (updating the "crumbled" graphic definitions is probably not really needed,
+     as animations for crumbled graphics can't be longer than one EMC cycle) */
+#if 1
+  set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
+                          sync_frame);
+
+#else
+
+  g_em->crumbled_bitmap = NULL;
+  g_em->crumbled_src_x = 0;
+  g_em->crumbled_src_y = 0;
+
+  g_em->has_crumbled_graphics = FALSE;
+
+  if (has_crumbled_graphics && crumbled != IMG_EMPTY_SPACE)
+  {
+    int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
+                                          g_crumbled->anim_delay,
+                                          g_crumbled->anim_mode,
+                                          g_crumbled->anim_start_frame,
+                                          sync_frame);
+
+    getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
+                    &g_em->crumbled_src_x, &g_em->crumbled_src_y);
+
+    g_em->has_crumbled_graphics = TRUE;
+  }
+#endif
+
+#if 0
+ {
+   int effective_action = xxx_effective_action;
+   int has_action_graphics = xxx_has_action_graphics;
+
+      if ((!g->double_movement && (effective_action == ACTION_FALLING ||
+                                  effective_action == ACTION_MOVING  ||
+                                  effective_action == ACTION_PUSHING ||
+                                  effective_action == ACTION_EATING)) ||
+         (!has_action_graphics && (effective_action == ACTION_FILLING ||
+                                   effective_action == ACTION_EMPTYING)))
+      {
+       int move_dir =
+         (effective_action == ACTION_FALLING ||
+          effective_action == ACTION_FILLING ||
+          effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
+       int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
+       int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
+       int num_steps = (i == Ydrip_s1  ? 16 :
+                        i == Ydrip_s1B ? 16 :
+                        i == Ydrip_s2  ? 16 :
+                        i == Ydrip_s2B ? 16 :
+                        i == Xsand_stonein_1 ? 32 :
+                        i == Xsand_stonein_2 ? 32 :
+                        i == Xsand_stonein_3 ? 32 :
+                        i == Xsand_stonein_4 ? 32 :
+                        i == Xsand_stoneout_1 ? 16 :
+                        i == Xsand_stoneout_2 ? 16 : 8);
+       int cx = ABS(dx) * (TILEX / num_steps);
+       int cy = ABS(dy) * (TILEY / num_steps);
+       int step_frame = (i == Ydrip_s2         ? j + 8 :
+                         i == Ydrip_s2B        ? j + 8 :
+                         i == Xsand_stonein_2  ? j + 8 :
+                         i == Xsand_stonein_3  ? j + 16 :
+                         i == Xsand_stonein_4  ? j + 24 :
+                         i == Xsand_stoneout_2 ? j + 8 : j) + 1;
+       int step = (is_backside ? step_frame : num_steps - step_frame);
+
+       if (is_backside)        /* tile where movement starts */
+       {
+         if (dx < 0 || dy < 0)
+         {
+           g_em->src_offset_x = cx * step;
+           g_em->src_offset_y = cy * step;
+         }
+         else
+         {
+           g_em->dst_offset_x = cx * step;
+           g_em->dst_offset_y = cy * step;
+         }
+       }
+       else                    /* tile where movement ends */
+       {
+         if (dx < 0 || dy < 0)
+         {
+           g_em->dst_offset_x = cx * step;
+           g_em->dst_offset_y = cy * step;
+         }
+         else
+         {
+           g_em->src_offset_x = cx * step;
+           g_em->src_offset_y = cy * step;
+         }
+       }
+
+       g_em->width  = TILEX - cx * step;
+       g_em->height = TILEY - cy * step;
+      }
+
+      /* create unique graphic identifier to decide if tile must be redrawn */
+      /* bit 31 - 16 (16 bit): EM style graphic
+        bit 15 - 12 ( 4 bit): EM style frame
+        bit 11 -  6 ( 6 bit): graphic width
+        bit  5 -  0 ( 6 bit): graphic height */
+      g_em->unique_identifier =
+       (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
+ }
+#endif
+
 }
 
 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,