added centering levels that are smaller than the playfield (MM engine)
[rocksndiamonds.git] / src / tools.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // tools.c
10 // ============================================================================
11
12 #include <math.h>
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "init.h"
18 #include "game.h"
19 #include "events.h"
20 #include "anim.h"
21 #include "network.h"
22 #include "tape.h"
23 #include "screens.h"
24
25
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX            FALSE
28 #define DEBUG_FRAME_TIME        FALSE
29
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES        0
32 #define TOOL_CTRL_ID_NO         1
33 #define TOOL_CTRL_ID_CONFIRM    2
34 #define TOOL_CTRL_ID_PLAYER_1   3
35 #define TOOL_CTRL_ID_PLAYER_2   4
36 #define TOOL_CTRL_ID_PLAYER_3   5
37 #define TOOL_CTRL_ID_PLAYER_4   6
38
39 #define NUM_TOOL_BUTTONS        7
40
41 /* constants for number of doors and door parts */
42 #define NUM_DOORS               2
43 #define NUM_PANELS              NUM_DOORS
44 // #define NUM_PANELS           0
45 #define MAX_PARTS_PER_DOOR      8
46 #define MAX_DOOR_PARTS          (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i)   ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
48
49
50 struct DoorPartOrderInfo
51 {
52   int nr;
53   int sort_priority;
54 };
55
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57
58 struct DoorPartControlInfo
59 {
60   int door_token;
61   int graphic;
62   struct DoorPartPosInfo *pos;
63 };
64
65 static struct DoorPartControlInfo door_part_controls[] =
66 {
67   {
68     DOOR_1,
69     IMG_GFX_DOOR_1_PART_1,
70     &door_1.part_1
71   },
72   {
73     DOOR_1,
74     IMG_GFX_DOOR_1_PART_2,
75     &door_1.part_2
76   },
77   {
78     DOOR_1,
79     IMG_GFX_DOOR_1_PART_3,
80     &door_1.part_3
81   },
82   {
83     DOOR_1,
84     IMG_GFX_DOOR_1_PART_4,
85     &door_1.part_4
86   },
87   {
88     DOOR_1,
89     IMG_GFX_DOOR_1_PART_5,
90     &door_1.part_5
91   },
92   {
93     DOOR_1,
94     IMG_GFX_DOOR_1_PART_6,
95     &door_1.part_6
96   },
97   {
98     DOOR_1,
99     IMG_GFX_DOOR_1_PART_7,
100     &door_1.part_7
101   },
102   {
103     DOOR_1,
104     IMG_GFX_DOOR_1_PART_8,
105     &door_1.part_8
106   },
107
108   {
109     DOOR_2,
110     IMG_GFX_DOOR_2_PART_1,
111     &door_2.part_1
112   },
113   {
114     DOOR_2,
115     IMG_GFX_DOOR_2_PART_2,
116     &door_2.part_2
117   },
118   {
119     DOOR_2,
120     IMG_GFX_DOOR_2_PART_3,
121     &door_2.part_3
122   },
123   {
124     DOOR_2,
125     IMG_GFX_DOOR_2_PART_4,
126     &door_2.part_4
127   },
128   {
129     DOOR_2,
130     IMG_GFX_DOOR_2_PART_5,
131     &door_2.part_5
132   },
133   {
134     DOOR_2,
135     IMG_GFX_DOOR_2_PART_6,
136     &door_2.part_6
137   },
138   {
139     DOOR_2,
140     IMG_GFX_DOOR_2_PART_7,
141     &door_2.part_7
142   },
143   {
144     DOOR_2,
145     IMG_GFX_DOOR_2_PART_8,
146     &door_2.part_8
147   },
148
149   {
150     DOOR_1,
151     IMG_BACKGROUND_PANEL,
152     &door_1.panel
153   },
154   {
155     DOOR_2,
156     IMG_BACKGROUND_TAPE,
157     &door_2.panel
158   },
159
160   {
161     -1,
162     -1,
163     NULL
164   }
165 };
166
167
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
173
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
176
177 static char *print_if_not_empty(int element)
178 {
179   static char *s = NULL;
180   char *token_name = element_info[element].token_name;
181
182   if (s != NULL)
183     free(s);
184
185   s = checked_malloc(strlen(token_name) + 10 + 1);
186
187   if (element != EL_EMPTY)
188     sprintf(s, "%d\t['%s']", element, token_name);
189   else
190     sprintf(s, "%d", element);
191
192   return s;
193 }
194
195 int correctLevelPosX_EM(int lx)
196 {
197   lx -= 1;
198   lx -= (BorderElement != EL_EMPTY ? 1 : 0);
199
200   return lx;
201 }
202
203 int correctLevelPosY_EM(int ly)
204 {
205   ly -= 1;
206   ly -= (BorderElement != EL_EMPTY ? 1 : 0);
207
208   return ly;
209 }
210
211 static int getFieldbufferOffsetX_RND()
212 {
213   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214   int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215   int dx_var = dx * TILESIZE_VAR / TILESIZE;
216   int fx = FX;
217
218   if (EVEN(SCR_FIELDX))
219   {
220     int ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
221
222     if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223       fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
224     else
225       fx += (dx_var > 0 ? TILEX_VAR : 0);
226   }
227   else
228   {
229     fx += dx_var;
230   }
231
232   if (full_lev_fieldx <= SCR_FIELDX)
233   {
234     if (EVEN(SCR_FIELDX))
235       fx = 2 * TILEX_VAR - (ODD(lev_fieldx)  ? TILEX_VAR / 2 : 0);
236     else
237       fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
238   }
239
240   return fx;
241 }
242
243 static int getFieldbufferOffsetY_RND()
244 {
245   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246   int dy = (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
247   int dy_var = dy * TILESIZE_VAR / TILESIZE;
248   int fy = FY;
249
250   if (EVEN(SCR_FIELDY))
251   {
252     int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
253
254     if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255       fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
256     else
257       fy += (dy_var > 0 ? TILEY_VAR : 0);
258   }
259   else
260   {
261     fy += dy_var;
262   }
263
264   if (full_lev_fieldy <= SCR_FIELDY)
265   {
266     if (EVEN(SCR_FIELDY))
267       fy = 2 * TILEY_VAR - (ODD(lev_fieldy)  ? TILEY_VAR / 2 : 0);
268     else
269       fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
270   }
271
272   return fy;
273 }
274
275 static int getLevelFromScreenX_RND(int sx)
276 {
277   int fx = getFieldbufferOffsetX_RND();
278   int dx = fx - FX;
279   int px = sx - SX;
280   int lx = LEVELX((px + dx) / TILESIZE_VAR);
281
282   return lx;
283 }
284
285 static int getLevelFromScreenY_RND(int sy)
286 {
287   int fy = getFieldbufferOffsetY_RND();
288   int dy = fy - FY;
289   int py = sy - SY;
290   int ly = LEVELY((py + dy) / TILESIZE_VAR);
291
292   return ly;
293 }
294
295 static int getLevelFromScreenX_EM(int sx)
296 {
297   int level_xsize = level.native_em_level->lev->width;
298   int full_xsize = level_xsize * TILESIZE_VAR;
299
300   sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
301
302   int fx = getFieldbufferOffsetX_EM();
303   int dx = fx;
304   int px = sx - SX;
305   int lx = LEVELX((px + dx) / TILESIZE_VAR);
306
307   lx = correctLevelPosX_EM(lx);
308
309   return lx;
310 }
311
312 static int getLevelFromScreenY_EM(int sy)
313 {
314   int level_ysize = level.native_em_level->lev->height;
315   int full_ysize = level_ysize * TILESIZE_VAR;
316
317   sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
318
319   int fy = getFieldbufferOffsetY_EM();
320   int dy = fy;
321   int py = sy - SY;
322   int ly = LEVELY((py + dy) / TILESIZE_VAR);
323
324   ly = correctLevelPosY_EM(ly);
325
326   return ly;
327 }
328
329 static int getLevelFromScreenX_SP(int sx)
330 {
331   int menBorder = setup.sp_show_border_elements;
332   int level_xsize = level.native_sp_level->width;
333   int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334
335   sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
336
337   int fx = getFieldbufferOffsetX_SP();
338   int dx = fx - FX;
339   int px = sx - SX;
340   int lx = LEVELX((px + dx) / TILESIZE_VAR);
341
342   return lx;
343 }
344
345 static int getLevelFromScreenY_SP(int sy)
346 {
347   int menBorder = setup.sp_show_border_elements;
348   int level_ysize = level.native_sp_level->height;
349   int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
350
351   sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
352
353   int fy = getFieldbufferOffsetY_SP();
354   int dy = fy - FY;
355   int py = sy - SY;
356   int ly = LEVELY((py + dy) / TILESIZE_VAR);
357
358   return ly;
359 }
360
361 static int getLevelFromScreenX_MM(int sx)
362 {
363   int level_xsize = level.native_mm_level->fieldx;
364   int full_xsize = level_xsize * TILESIZE_VAR;
365
366   sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
367
368   int px = sx - SX;
369   int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
370
371   return lx;
372 }
373
374 static int getLevelFromScreenY_MM(int sy)
375 {
376   int level_ysize = level.native_mm_level->fieldy;
377   int full_ysize = level_ysize * TILESIZE_VAR;
378
379   sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
380
381   int py = sy - SY;
382   int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
383
384   return ly;
385 }
386
387 int getLevelFromScreenX(int x)
388 {
389   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390     return getLevelFromScreenX_EM(x);
391   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392     return getLevelFromScreenX_SP(x);
393   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394     return getLevelFromScreenX_MM(x);
395   else
396     return getLevelFromScreenX_RND(x);
397 }
398
399 int getLevelFromScreenY(int y)
400 {
401   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402     return getLevelFromScreenY_EM(y);
403   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404     return getLevelFromScreenY_SP(y);
405   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406     return getLevelFromScreenY_MM(y);
407   else
408     return getLevelFromScreenY_RND(y);
409 }
410
411 void DumpTile(int x, int y)
412 {
413   int sx = SCREENX(x);
414   int sy = SCREENY(y);
415   char *token_name;
416
417   printf_line("-", 79);
418   printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419   printf_line("-", 79);
420
421   if (!IN_LEV_FIELD(x, y))
422   {
423     printf("(not in level field)\n");
424     printf("\n");
425
426     return;
427   }
428
429   token_name = element_info[Feld[x][y]].token_name;
430
431   printf("  Feld:        %d\t['%s']\n", Feld[x][y], token_name);
432   printf("  Back:        %s\n", print_if_not_empty(Back[x][y]));
433   printf("  Store:       %s\n", print_if_not_empty(Store[x][y]));
434   printf("  Store2:      %s\n", print_if_not_empty(Store2[x][y]));
435   printf("  StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
436   printf("  MovPos:      %d\n", MovPos[x][y]);
437   printf("  MovDir:      %d\n", MovDir[x][y]);
438   printf("  MovDelay:    %d\n", MovDelay[x][y]);
439   printf("  ChangeDelay: %d\n", ChangeDelay[x][y]);
440   printf("  CustomValue: %d\n", CustomValue[x][y]);
441   printf("  GfxElement:  %d\n", GfxElement[x][y]);
442   printf("  GfxAction:   %d\n", GfxAction[x][y]);
443   printf("  GfxFrame:    %d [%d]\n", GfxFrame[x][y], FrameCounter);
444   printf("  Player x/y:  %d, %d\n", local_player->jx, local_player->jy);
445   printf("\n");
446 }
447
448 void DumpTileFromScreen(int sx, int sy)
449 {
450   int lx = getLevelFromScreenX(sx);
451   int ly = getLevelFromScreenY(sy);
452
453   DumpTile(lx, ly);
454 }
455
456 void SetDrawtoField(int mode)
457 {
458   if (mode == DRAW_TO_FIELDBUFFER)
459   {
460     FX = 2 * TILEX_VAR;
461     FY = 2 * TILEY_VAR;
462     BX1 = -2;
463     BY1 = -2;
464     BX2 = SCR_FIELDX + 1;
465     BY2 = SCR_FIELDY + 1;
466
467     drawto_field = fieldbuffer;
468   }
469   else  /* DRAW_TO_BACKBUFFER */
470   {
471     FX = SX;
472     FY = SY;
473     BX1 = 0;
474     BY1 = 0;
475     BX2 = SCR_FIELDX - 1;
476     BY2 = SCR_FIELDY - 1;
477
478     drawto_field = backbuffer;
479   }
480 }
481
482 static void RedrawPlayfield_RND()
483 {
484   if (game.envelope_active)
485     return;
486
487   DrawLevel(REDRAW_ALL);
488   DrawAllPlayers();
489 }
490
491 void RedrawPlayfield()
492 {
493   if (game_status != GAME_MODE_PLAYING)
494     return;
495
496   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
497     RedrawPlayfield_EM(TRUE);
498   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
499     RedrawPlayfield_SP(TRUE);
500   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
501     RedrawPlayfield_MM();
502   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
503     RedrawPlayfield_RND();
504
505   BlitScreenToBitmap(backbuffer);
506
507   BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
508              gfx.sx, gfx.sy);
509 }
510
511 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512                                      int draw_target)
513 {
514   Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
515   Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
516
517   if (x == -1 && y == -1)
518     return;
519
520   if (draw_target == DRAW_TO_SCREEN)
521     BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
522   else
523     BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 }
525
526 static void DrawMaskedBorderExt_FIELD(int draw_target)
527 {
528   if (global.border_status >= GAME_MODE_MAIN &&
529       global.border_status <= GAME_MODE_PLAYING &&
530       border.draw_masked[global.border_status])
531     DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
532                              draw_target);
533 }
534
535 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
536 {
537   // when drawing to backbuffer, never draw border over open doors
538   if (draw_target == DRAW_TO_BACKBUFFER &&
539       (GetDoorState() & DOOR_OPEN_1))
540     return;
541
542   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543       (global.border_status != GAME_MODE_EDITOR ||
544        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
545     DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 }
547
548 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
549 {
550   // when drawing to backbuffer, never draw border over open doors
551   if (draw_target == DRAW_TO_BACKBUFFER &&
552       (GetDoorState() & DOOR_OPEN_2))
553     return;
554
555   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556       global.border_status != GAME_MODE_EDITOR)
557     DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 }
559
560 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
561 {
562   /* currently not available */
563 }
564
565 static void DrawMaskedBorderExt_ALL(int draw_target)
566 {
567   DrawMaskedBorderExt_FIELD(draw_target);
568   DrawMaskedBorderExt_DOOR_1(draw_target);
569   DrawMaskedBorderExt_DOOR_2(draw_target);
570   DrawMaskedBorderExt_DOOR_3(draw_target);
571 }
572
573 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
574 {
575   /* never draw masked screen borders on borderless screens */
576   if (global.border_status == GAME_MODE_LOADING ||
577       global.border_status == GAME_MODE_TITLE)
578     return;
579
580   if (redraw_mask & REDRAW_ALL)
581     DrawMaskedBorderExt_ALL(draw_target);
582   else
583   {
584     if (redraw_mask & REDRAW_FIELD)
585       DrawMaskedBorderExt_FIELD(draw_target);
586     if (redraw_mask & REDRAW_DOOR_1)
587       DrawMaskedBorderExt_DOOR_1(draw_target);
588     if (redraw_mask & REDRAW_DOOR_2)
589       DrawMaskedBorderExt_DOOR_2(draw_target);
590     if (redraw_mask & REDRAW_DOOR_3)
591       DrawMaskedBorderExt_DOOR_3(draw_target);
592   }
593 }
594
595 void DrawMaskedBorder_FIELD()
596 {
597   DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 }
599
600 void DrawMaskedBorder(int redraw_mask)
601 {
602   DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 }
604
605 void DrawMaskedBorderToTarget(int draw_target)
606 {
607   if (draw_target == DRAW_TO_BACKBUFFER ||
608       draw_target == DRAW_TO_SCREEN)
609   {
610     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
611   }
612   else
613   {
614     int last_border_status = global.border_status;
615
616     if (draw_target == DRAW_TO_FADE_SOURCE)
617     {
618       global.border_status = gfx.fade_border_source_status;
619       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
620     }
621     else if (draw_target == DRAW_TO_FADE_TARGET)
622     {
623       global.border_status = gfx.fade_border_target_status;
624       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625     }
626
627     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
628
629     global.border_status = last_border_status;
630     gfx.masked_border_bitmap_ptr = backbuffer;
631   }
632 }
633
634 void DrawTileCursor(int draw_target)
635 {
636   Bitmap *fade_bitmap;
637   Bitmap *src_bitmap;
638   int src_x, src_y;
639   int dst_x, dst_y;
640   int graphic = IMG_GLOBAL_TILE_CURSOR;
641   int frame = 0;
642   int tilesize = TILESIZE_VAR;
643   int width = tilesize;
644   int height = tilesize;
645
646   if (game_status != GAME_MODE_PLAYING)
647     return;
648
649   if (!tile_cursor.enabled ||
650       !tile_cursor.active)
651     return;
652
653   if (tile_cursor.moving)
654   {
655     int step = TILESIZE_VAR / 4;
656     int dx = tile_cursor.target_x - tile_cursor.x;
657     int dy = tile_cursor.target_y - tile_cursor.y;
658
659     if (ABS(dx) < step)
660       tile_cursor.x = tile_cursor.target_x;
661     else
662       tile_cursor.x += SIGN(dx) * step;
663
664     if (ABS(dy) < step)
665       tile_cursor.y = tile_cursor.target_y;
666     else
667       tile_cursor.y += SIGN(dy) * step;
668
669     if (tile_cursor.x == tile_cursor.target_x &&
670         tile_cursor.y == tile_cursor.target_y)
671       tile_cursor.moving = FALSE;
672   }
673
674   dst_x = tile_cursor.x;
675   dst_y = tile_cursor.y;
676
677   frame = getGraphicAnimationFrame(graphic, -1);
678
679   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
680
681   fade_bitmap =
682     (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
683      draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
684
685   if (draw_target == DRAW_TO_SCREEN)
686     BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
687   else
688     BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
689                      dst_x, dst_y);
690 }
691
692 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
693 {
694   int fx = getFieldbufferOffsetX_RND();
695   int fy = getFieldbufferOffsetY_RND();
696
697   BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
698 }
699
700 void BlitScreenToBitmap(Bitmap *target_bitmap)
701 {
702   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
703     BlitScreenToBitmap_EM(target_bitmap);
704   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
705     BlitScreenToBitmap_SP(target_bitmap);
706   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
707     BlitScreenToBitmap_MM(target_bitmap);
708   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
709     BlitScreenToBitmap_RND(target_bitmap);
710
711   redraw_mask |= REDRAW_FIELD;
712 }
713
714 void DrawFramesPerSecond()
715 {
716   char text[100];
717   int font_nr = FONT_TEXT_2;
718   int font_width = getFontWidth(font_nr);
719   int draw_deactivation_mask = GetDrawDeactivationMask();
720   boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
721
722   /* draw FPS with leading space (needed if field buffer deactivated) */
723   sprintf(text, " %04.1f fps", global.frames_per_second);
724
725   /* override draw deactivation mask (required for invisible warp mode) */
726   SetDrawDeactivationMask(REDRAW_NONE);
727
728   /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
729   DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
730               font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
731
732   /* set draw deactivation mask to previous value */
733   SetDrawDeactivationMask(draw_deactivation_mask);
734
735   /* force full-screen redraw in this frame */
736   redraw_mask = REDRAW_ALL;
737 }
738
739 #if DEBUG_FRAME_TIME
740 static void PrintFrameTimeDebugging()
741 {
742   static unsigned int last_counter = 0;
743   unsigned int counter = Counter();
744   int diff_1 = counter - last_counter;
745   int diff_2 = diff_1 - GAME_FRAME_DELAY;
746   int diff_2_max = 20;
747   int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
748   char diff_bar[2 * diff_2_max + 5];
749   int pos = 0;
750   int i;
751
752   diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
753
754   for (i = 0; i < diff_2_max; i++)
755     diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
756                        i >= diff_2_max - diff_2_cut ? '-' : ' ');
757
758   diff_bar[pos++] = '|';
759
760   for (i = 0; i < diff_2_max; i++)
761     diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
762
763   diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
764
765   diff_bar[pos++] = '\0';
766
767   Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
768         counter,
769         diff_1,
770         (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
771         diff_bar);
772
773   last_counter = counter;
774 }
775 #endif
776
777 static int unifiedRedrawMask(int mask)
778 {
779   if (mask & REDRAW_ALL)
780     return REDRAW_ALL;
781
782   if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
783     return REDRAW_ALL;
784
785   return mask;
786 }
787
788 static boolean equalRedrawMasks(int mask_1, int mask_2)
789 {
790   return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
791 }
792
793 void BackToFront()
794 {
795   static int last_redraw_mask = REDRAW_NONE;
796
797   // force screen redraw in every frame to continue drawing global animations
798   // (but always use the last redraw mask to prevent unwanted side effects)
799   if (redraw_mask == REDRAW_NONE)
800     redraw_mask = last_redraw_mask;
801
802   last_redraw_mask = redraw_mask;
803
804 #if 1
805   // masked border now drawn immediately when blitting backbuffer to window
806 #else
807   // draw masked border to all viewports, if defined
808   DrawMaskedBorder(redraw_mask);
809 #endif
810
811   // draw frames per second (only if debug mode is enabled)
812   if (redraw_mask & REDRAW_FPS)
813     DrawFramesPerSecond();
814
815   // remove playfield redraw before potentially merging with doors redraw
816   if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
817     redraw_mask &= ~REDRAW_FIELD;
818
819   // redraw complete window if both playfield and (some) doors need redraw
820   if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
821     redraw_mask = REDRAW_ALL;
822
823   /* although redrawing the whole window would be fine for normal gameplay,
824      being able to only redraw the playfield is required for deactivating
825      certain drawing areas (mainly playfield) to work, which is needed for
826      warp-forward to be fast enough (by skipping redraw of most frames) */
827
828   if (redraw_mask & REDRAW_ALL)
829   {
830     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
831   }
832   else if (redraw_mask & REDRAW_FIELD)
833   {
834     BlitBitmap(backbuffer, window,
835                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
836   }
837   else if (redraw_mask & REDRAW_DOORS)
838   {
839     // merge door areas to prevent calling screen redraw more than once
840     int x1 = WIN_XSIZE;
841     int y1 = WIN_YSIZE;
842     int x2 = 0;
843     int y2 = 0;
844
845     if (redraw_mask & REDRAW_DOOR_1)
846     {
847       x1 = MIN(x1, DX);
848       y1 = MIN(y1, DY);
849       x2 = MAX(x2, DX + DXSIZE);
850       y2 = MAX(y2, DY + DYSIZE);
851     }
852
853     if (redraw_mask & REDRAW_DOOR_2)
854     {
855       x1 = MIN(x1, VX);
856       y1 = MIN(y1, VY);
857       x2 = MAX(x2, VX + VXSIZE);
858       y2 = MAX(y2, VY + VYSIZE);
859     }
860
861     if (redraw_mask & REDRAW_DOOR_3)
862     {
863       x1 = MIN(x1, EX);
864       y1 = MIN(y1, EY);
865       x2 = MAX(x2, EX + EXSIZE);
866       y2 = MAX(y2, EY + EYSIZE);
867     }
868
869     // make sure that at least one pixel is blitted, and inside the screen
870     // (else nothing is blitted, causing the animations not to be updated)
871     x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
872     y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
873     x2 = MIN(MAX(1, x2), WIN_XSIZE);
874     y2 = MIN(MAX(1, y2), WIN_YSIZE);
875
876     BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
877   }
878
879   redraw_mask = REDRAW_NONE;
880
881 #if DEBUG_FRAME_TIME
882   PrintFrameTimeDebugging();
883 #endif
884 }
885
886 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
887 {
888   unsigned int frame_delay_value_old = GetVideoFrameDelay();
889
890   SetVideoFrameDelay(frame_delay_value);
891
892   BackToFront();
893
894   SetVideoFrameDelay(frame_delay_value_old);
895 }
896
897 static int fade_type_skip = FADE_TYPE_NONE;
898
899 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
900 {
901   void (*draw_border_function)(void) = NULL;
902   int x, y, width, height;
903   int fade_delay, post_delay;
904
905   if (fade_type == FADE_TYPE_FADE_OUT)
906   {
907     if (fade_type_skip != FADE_TYPE_NONE)
908     {
909       /* skip all fade operations until specified fade operation */
910       if (fade_type & fade_type_skip)
911         fade_type_skip = FADE_TYPE_NONE;
912
913       return;
914     }
915
916     if (fading.fade_mode & FADE_TYPE_TRANSFORM)
917       return;
918   }
919
920   redraw_mask |= fade_mask;
921
922   if (fade_type == FADE_TYPE_SKIP)
923   {
924     fade_type_skip = fade_mode;
925
926     return;
927   }
928
929   fade_delay = fading.fade_delay;
930   post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
931
932   if (fade_type_skip != FADE_TYPE_NONE)
933   {
934     /* skip all fade operations until specified fade operation */
935     if (fade_type & fade_type_skip)
936       fade_type_skip = FADE_TYPE_NONE;
937
938     fade_delay = 0;
939   }
940
941   if (global.autoplay_leveldir)
942   {
943     return;
944   }
945
946   if (fade_mask == REDRAW_FIELD)
947   {
948     x = FADE_SX;
949     y = FADE_SY;
950     width  = FADE_SXSIZE;
951     height = FADE_SYSIZE;
952
953     if (border.draw_masked_when_fading)
954       draw_border_function = DrawMaskedBorder_FIELD;    /* update when fading */
955     else
956       DrawMaskedBorder_FIELD();                         /* draw once */
957   }
958   else          /* REDRAW_ALL */
959   {
960     x = 0;
961     y = 0;
962     width  = WIN_XSIZE;
963     height = WIN_YSIZE;
964   }
965
966   if (!setup.fade_screens ||
967       fade_delay == 0 ||
968       fading.fade_mode == FADE_MODE_NONE)
969   {
970     if (fade_mode == FADE_MODE_FADE_OUT)
971       return;
972
973     BlitBitmap(backbuffer, window, x, y, width, height, x, y);
974
975     redraw_mask &= ~fade_mask;
976
977     return;
978   }
979
980   FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981                 draw_border_function);
982
983   redraw_mask &= ~fade_mask;
984 }
985
986 static void SetScreenStates_BeforeFadingIn()
987 {
988   // temporarily set screen mode for animations to screen after fading in
989   global.anim_status = global.anim_status_next;
990
991   // store backbuffer with all animations that will be started after fading in
992   if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
993     PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
994
995   // set screen mode for animations back to fading
996   global.anim_status = GAME_MODE_PSEUDO_FADING;
997 }
998
999 static void SetScreenStates_AfterFadingIn()
1000 {
1001   // store new source screen (to use correct masked border for fading)
1002   gfx.fade_border_source_status = global.border_status;
1003
1004   global.anim_status = global.anim_status_next;
1005 }
1006
1007 static void SetScreenStates_BeforeFadingOut()
1008 {
1009   // store new target screen (to use correct masked border for fading)
1010   gfx.fade_border_target_status = game_status;
1011
1012   // set screen mode for animations to fading
1013   global.anim_status = GAME_MODE_PSEUDO_FADING;
1014
1015   // store backbuffer with all animations that will be stopped for fading out
1016   if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1017     PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1018 }
1019
1020 static void SetScreenStates_AfterFadingOut()
1021 {
1022   global.border_status = game_status;
1023 }
1024
1025 void FadeIn(int fade_mask)
1026 {
1027   SetScreenStates_BeforeFadingIn();
1028
1029 #if 1
1030   DrawMaskedBorder(REDRAW_ALL);
1031 #endif
1032
1033   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1034     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1035   else
1036     FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1037
1038   FADE_SX = REAL_SX;
1039   FADE_SY = REAL_SY;
1040   FADE_SXSIZE = FULL_SXSIZE;
1041   FADE_SYSIZE = FULL_SYSIZE;
1042
1043   if (game_status == GAME_MODE_PLAYING &&
1044       strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1045     SetOverlayActive(TRUE);
1046
1047   SetScreenStates_AfterFadingIn();
1048
1049   // force update of global animation status in case of rapid screen changes
1050   redraw_mask = REDRAW_ALL;
1051   BackToFront();
1052 }
1053
1054 void FadeOut(int fade_mask)
1055 {
1056   // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1057   if (!equalRedrawMasks(fade_mask, redraw_mask))
1058     BackToFront();
1059
1060   SetScreenStates_BeforeFadingOut();
1061
1062   SetTileCursorActive(FALSE);
1063   SetOverlayActive(FALSE);
1064
1065 #if 0
1066   DrawMaskedBorder(REDRAW_ALL);
1067 #endif
1068
1069   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1070     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1071   else
1072     FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1073
1074   SetScreenStates_AfterFadingOut();
1075 }
1076
1077 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1078 {
1079   static struct TitleFadingInfo fading_leave_stored;
1080
1081   if (set)
1082     fading_leave_stored = fading_leave;
1083   else
1084     fading = fading_leave_stored;
1085 }
1086
1087 void FadeSetEnterMenu()
1088 {
1089   fading = menu.enter_menu;
1090
1091   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
1092 }
1093
1094 void FadeSetLeaveMenu()
1095 {
1096   fading = menu.leave_menu;
1097
1098   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
1099 }
1100
1101 void FadeSetEnterScreen()
1102 {
1103   fading = menu.enter_screen[game_status];
1104
1105   FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);       /* store */
1106 }
1107
1108 void FadeSetNextScreen()
1109 {
1110   fading = menu.next_screen[game_status];
1111
1112   // (do not overwrite fade mode set by FadeSetEnterScreen)
1113   // FadeSetLeaveNext(fading, TRUE);    /* (keep same fade mode) */
1114 }
1115
1116 void FadeSetLeaveScreen()
1117 {
1118   FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);      /* recall */
1119 }
1120
1121 void FadeSetFromType(int type)
1122 {
1123   if (type & TYPE_ENTER_SCREEN)
1124     FadeSetEnterScreen();
1125   else if (type & TYPE_ENTER)
1126     FadeSetEnterMenu();
1127   else if (type & TYPE_LEAVE)
1128     FadeSetLeaveMenu();
1129 }
1130
1131 void FadeSetDisabled()
1132 {
1133   static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1134
1135   fading = fading_none;
1136 }
1137
1138 void FadeSkipNextFadeIn()
1139 {
1140   FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1141 }
1142
1143 void FadeSkipNextFadeOut()
1144 {
1145   FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1146 }
1147
1148 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1149 {
1150   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1151
1152   return (graphic == IMG_UNDEFINED ? NULL :
1153           graphic_info[graphic].bitmap != NULL || redefined ?
1154           graphic_info[graphic].bitmap :
1155           graphic_info[default_graphic].bitmap);
1156 }
1157
1158 Bitmap *getBackgroundBitmap(int graphic)
1159 {
1160   return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1161 }
1162
1163 Bitmap *getGlobalBorderBitmap(int graphic)
1164 {
1165   return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1166 }
1167
1168 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1169 {
1170   int graphic =
1171     (status == GAME_MODE_MAIN ||
1172      status == GAME_MODE_PSEUDO_TYPENAME        ? IMG_GLOBAL_BORDER_MAIN :
1173      status == GAME_MODE_SCORES                 ? IMG_GLOBAL_BORDER_SCORES :
1174      status == GAME_MODE_EDITOR                 ? IMG_GLOBAL_BORDER_EDITOR :
1175      status == GAME_MODE_PLAYING                ? IMG_GLOBAL_BORDER_PLAYING :
1176      IMG_GLOBAL_BORDER);
1177
1178   return getGlobalBorderBitmap(graphic);
1179 }
1180
1181 void SetWindowBackgroundImageIfDefined(int graphic)
1182 {
1183   if (graphic_info[graphic].bitmap)
1184     SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1185 }
1186
1187 void SetMainBackgroundImageIfDefined(int graphic)
1188 {
1189   if (graphic_info[graphic].bitmap)
1190     SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1191 }
1192
1193 void SetDoorBackgroundImageIfDefined(int graphic)
1194 {
1195   if (graphic_info[graphic].bitmap)
1196     SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1197 }
1198
1199 void SetWindowBackgroundImage(int graphic)
1200 {
1201   SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1202 }
1203
1204 void SetMainBackgroundImage(int graphic)
1205 {
1206   SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1207 }
1208
1209 void SetDoorBackgroundImage(int graphic)
1210 {
1211   SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1212 }
1213
1214 void SetPanelBackground()
1215 {
1216   struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1217
1218   BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1219                   gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1220
1221   SetDoorBackgroundBitmap(bitmap_db_panel);
1222 }
1223
1224 void DrawBackground(int x, int y, int width, int height)
1225 {
1226   /* "drawto" might still point to playfield buffer here (hall of fame) */
1227   ClearRectangleOnBackground(backbuffer, x, y, width, height);
1228
1229   if (IN_GFX_FIELD_FULL(x, y))
1230     redraw_mask |= REDRAW_FIELD;
1231   else if (IN_GFX_DOOR_1(x, y))
1232     redraw_mask |= REDRAW_DOOR_1;
1233   else if (IN_GFX_DOOR_2(x, y))
1234     redraw_mask |= REDRAW_DOOR_2;
1235   else if (IN_GFX_DOOR_3(x, y))
1236     redraw_mask |= REDRAW_DOOR_3;
1237 }
1238
1239 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1240 {
1241   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1242
1243   if (font->bitmap == NULL)
1244     return;
1245
1246   DrawBackground(x, y, width, height);
1247 }
1248
1249 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1250 {
1251   struct GraphicInfo *g = &graphic_info[graphic];
1252
1253   if (g->bitmap == NULL)
1254     return;
1255
1256   DrawBackground(x, y, width, height);
1257 }
1258
1259 static int game_status_last = -1;
1260 static Bitmap *global_border_bitmap_last = NULL;
1261 static Bitmap *global_border_bitmap = NULL;
1262 static int real_sx_last = -1, real_sy_last = -1;
1263 static int full_sxsize_last = -1, full_sysize_last = -1;
1264 static int dx_last = -1, dy_last = -1;
1265 static int dxsize_last = -1, dysize_last = -1;
1266 static int vx_last = -1, vy_last = -1;
1267 static int vxsize_last = -1, vysize_last = -1;
1268
1269 boolean CheckIfGlobalBorderHasChanged()
1270 {
1271   // if game status has not changed, global border has not changed either
1272   if (game_status == game_status_last)
1273     return FALSE;
1274
1275   // determine and store new global border bitmap for current game status
1276   global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1277
1278   return (global_border_bitmap_last != global_border_bitmap);
1279 }
1280
1281 boolean CheckIfGlobalBorderRedrawIsNeeded()
1282 {
1283   // if game status has not changed, nothing has to be redrawn
1284   if (game_status == game_status_last)
1285     return FALSE;
1286
1287   // redraw if last screen was title screen
1288   if (game_status_last == GAME_MODE_TITLE)
1289     return TRUE;
1290
1291   // redraw if global screen border has changed
1292   if (CheckIfGlobalBorderHasChanged())
1293     return TRUE;
1294
1295   // redraw if position or size of playfield area has changed
1296   if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1297       full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1298     return TRUE;
1299
1300   // redraw if position or size of door area has changed
1301   if (dx_last != DX || dy_last != DY ||
1302       dxsize_last != DXSIZE || dysize_last != DYSIZE)
1303     return TRUE;
1304
1305   // redraw if position or size of tape area has changed
1306   if (vx_last != VX || vy_last != VY ||
1307       vxsize_last != VXSIZE || vysize_last != VYSIZE)
1308     return TRUE;
1309
1310   return FALSE;
1311 }
1312
1313 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1314 {
1315   if (bitmap)
1316     BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1317   else
1318     ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1319 }
1320
1321 void RedrawGlobalBorder()
1322 {
1323   Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1324
1325   RedrawGlobalBorderFromBitmap(bitmap);
1326
1327   redraw_mask = REDRAW_ALL;
1328 }
1329
1330 static void RedrawGlobalBorderIfNeeded()
1331 {
1332   if (game_status == game_status_last)
1333     return;
1334
1335   // copy current draw buffer to later copy back areas that have not changed
1336   if (game_status_last != GAME_MODE_TITLE)
1337     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1338
1339   if (CheckIfGlobalBorderRedrawIsNeeded())
1340   {
1341     // redraw global screen border (or clear, if defined to be empty)
1342     RedrawGlobalBorderFromBitmap(global_border_bitmap);
1343
1344     // copy previous playfield and door areas, if they are defined on both
1345     // previous and current screen and if they still have the same size
1346
1347     if (real_sx_last != -1 && real_sy_last != -1 &&
1348         REAL_SX != -1 && REAL_SY != -1 &&
1349         full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1350       BlitBitmap(bitmap_db_store_1, backbuffer,
1351                  real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1352                  REAL_SX, REAL_SY);
1353
1354     if (dx_last != -1 && dy_last != -1 &&
1355         DX != -1 && DY != -1 &&
1356         dxsize_last == DXSIZE && dysize_last == DYSIZE)
1357       BlitBitmap(bitmap_db_store_1, backbuffer,
1358                  dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1359
1360     if (vx_last != -1 && vy_last != -1 &&
1361         VX != -1 && VY != -1 &&
1362         vxsize_last == VXSIZE && vysize_last == VYSIZE)
1363       BlitBitmap(bitmap_db_store_1, backbuffer,
1364                  vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1365
1366     redraw_mask = REDRAW_ALL;
1367   }
1368
1369   game_status_last = game_status;
1370
1371   global_border_bitmap_last = global_border_bitmap;
1372
1373   real_sx_last = REAL_SX;
1374   real_sy_last = REAL_SY;
1375   full_sxsize_last = FULL_SXSIZE;
1376   full_sysize_last = FULL_SYSIZE;
1377   dx_last = DX;
1378   dy_last = DY;
1379   dxsize_last = DXSIZE;
1380   dysize_last = DYSIZE;
1381   vx_last = VX;
1382   vy_last = VY;
1383   vxsize_last = VXSIZE;
1384   vysize_last = VYSIZE;
1385 }
1386
1387 void ClearField()
1388 {
1389   RedrawGlobalBorderIfNeeded();
1390
1391   /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1392   /* (when entering hall of fame after playing) */
1393   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1394
1395   /* !!! maybe this should be done before clearing the background !!! */
1396   if (game_status == GAME_MODE_PLAYING)
1397   {
1398     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1399     SetDrawtoField(DRAW_TO_FIELDBUFFER);
1400   }
1401   else
1402   {
1403     SetDrawtoField(DRAW_TO_BACKBUFFER);
1404   }
1405 }
1406
1407 void MarkTileDirty(int x, int y)
1408 {
1409   redraw_mask |= REDRAW_FIELD;
1410 }
1411
1412 void SetBorderElement()
1413 {
1414   int x, y;
1415
1416   BorderElement = EL_EMPTY;
1417
1418   /* the MM game engine does not use a visible border element */
1419   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1420     return;
1421
1422   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1423   {
1424     for (x = 0; x < lev_fieldx; x++)
1425     {
1426       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1427         BorderElement = EL_STEELWALL;
1428
1429       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1430         x = lev_fieldx - 2;
1431     }
1432   }
1433 }
1434
1435 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1436                        int max_array_fieldx, int max_array_fieldy,
1437                        short field[max_array_fieldx][max_array_fieldy],
1438                        int max_fieldx, int max_fieldy)
1439 {
1440   int i,x,y;
1441   int old_element;
1442   static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1443   static int safety = 0;
1444
1445   /* check if starting field still has the desired content */
1446   if (field[from_x][from_y] == fill_element)
1447     return;
1448
1449   safety++;
1450
1451   if (safety > max_fieldx * max_fieldy)
1452     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1453
1454   old_element = field[from_x][from_y];
1455   field[from_x][from_y] = fill_element;
1456
1457   for (i = 0; i < 4; i++)
1458   {
1459     x = from_x + check[i][0];
1460     y = from_y + check[i][1];
1461
1462     if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1463       FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1464                         field, max_fieldx, max_fieldy);
1465   }
1466
1467   safety--;
1468 }
1469
1470 void FloodFillLevel(int from_x, int from_y, int fill_element,
1471                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1472                     int max_fieldx, int max_fieldy)
1473 {
1474   FloodFillLevelExt(from_x, from_y, fill_element,
1475                     MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1476                     max_fieldx, max_fieldy);
1477 }
1478
1479 void SetRandomAnimationValue(int x, int y)
1480 {
1481   gfx.anim_random_frame = GfxRandom[x][y];
1482 }
1483
1484 int getGraphicAnimationFrame(int graphic, int sync_frame)
1485 {
1486   /* animation synchronized with global frame counter, not move position */
1487   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1488     sync_frame = FrameCounter;
1489
1490   return getAnimationFrame(graphic_info[graphic].anim_frames,
1491                            graphic_info[graphic].anim_delay,
1492                            graphic_info[graphic].anim_mode,
1493                            graphic_info[graphic].anim_start_frame,
1494                            sync_frame);
1495 }
1496
1497 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1498 {
1499   struct GraphicInfo *g = &graphic_info[graphic];
1500   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1501
1502   if (tilesize == gfx.standard_tile_size)
1503     *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1504   else if (tilesize == game.tile_size)
1505     *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1506   else
1507     *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1508 }
1509
1510 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1511                         boolean get_backside)
1512 {
1513   struct GraphicInfo *g = &graphic_info[graphic];
1514   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1515   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1516
1517   if (g->offset_y == 0)         /* frames are ordered horizontally */
1518   {
1519     int max_width = g->anim_frames_per_line * g->width;
1520     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1521
1522     *x = pos % max_width;
1523     *y = src_y % g->height + pos / max_width * g->height;
1524   }
1525   else if (g->offset_x == 0)    /* frames are ordered vertically */
1526   {
1527     int max_height = g->anim_frames_per_line * g->height;
1528     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1529
1530     *x = src_x % g->width + pos / max_height * g->width;
1531     *y = pos % max_height;
1532   }
1533   else                          /* frames are ordered diagonally */
1534   {
1535     *x = src_x + frame * g->offset_x;
1536     *y = src_y + frame * g->offset_y;
1537   }
1538 }
1539
1540 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1541                               Bitmap **bitmap, int *x, int *y,
1542                               boolean get_backside)
1543 {
1544   struct GraphicInfo *g = &graphic_info[graphic];
1545
1546   // if no in-game graphics defined, always use standard graphic size
1547   if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1548     tilesize = TILESIZE;
1549
1550   getGraphicSourceBitmap(graphic, tilesize, bitmap);
1551   getGraphicSourceXY(graphic, frame, x, y, get_backside);
1552
1553   *x = *x * tilesize / g->tile_size;
1554   *y = *y * tilesize / g->tile_size;
1555 }
1556
1557 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1558                            Bitmap **bitmap, int *x, int *y)
1559 {
1560   getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1561 }
1562
1563 void getFixedGraphicSource(int graphic, int frame,
1564                            Bitmap **bitmap, int *x, int *y)
1565 {
1566   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1567 }
1568
1569 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1570 {
1571   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1572 }
1573
1574 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1575                                        int *x, int *y, boolean get_backside)
1576 {
1577   getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1578                            get_backside);
1579 }
1580
1581 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1582 {
1583   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1584 }
1585
1586 void DrawGraphic(int x, int y, int graphic, int frame)
1587 {
1588 #if DEBUG
1589   if (!IN_SCR_FIELD(x, y))
1590   {
1591     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1592     printf("DrawGraphic(): This should never happen!\n");
1593     return;
1594   }
1595 #endif
1596
1597   DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1598                  frame);
1599
1600   MarkTileDirty(x, y);
1601 }
1602
1603 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1604 {
1605 #if DEBUG
1606   if (!IN_SCR_FIELD(x, y))
1607   {
1608     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1609     printf("DrawGraphic(): This should never happen!\n");
1610     return;
1611   }
1612 #endif
1613
1614   DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1615                       frame);
1616   MarkTileDirty(x, y);
1617 }
1618
1619 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1620                     int frame)
1621 {
1622   Bitmap *src_bitmap;
1623   int src_x, src_y;
1624
1625   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1626
1627   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1628 }
1629
1630 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1631                          int frame)
1632 {
1633   Bitmap *src_bitmap;
1634   int src_x, src_y;
1635
1636   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1637   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1638 }
1639
1640 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1641 {
1642 #if DEBUG
1643   if (!IN_SCR_FIELD(x, y))
1644   {
1645     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1646     printf("DrawGraphicThruMask(): This should never happen!\n");
1647     return;
1648   }
1649 #endif
1650
1651   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1652                          graphic, frame);
1653
1654   MarkTileDirty(x, y);
1655 }
1656
1657 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1658 {
1659 #if DEBUG
1660   if (!IN_SCR_FIELD(x, y))
1661   {
1662     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1663     printf("DrawGraphicThruMask(): This should never happen!\n");
1664     return;
1665   }
1666 #endif
1667
1668   DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1669                               graphic, frame);
1670   MarkTileDirty(x, y);
1671 }
1672
1673 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1674                             int frame)
1675 {
1676   Bitmap *src_bitmap;
1677   int src_x, src_y;
1678
1679   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1680
1681   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1682                    dst_x, dst_y);
1683 }
1684
1685 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1686                                  int graphic, int frame)
1687 {
1688   Bitmap *src_bitmap;
1689   int src_x, src_y;
1690
1691   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1692
1693   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1694                    dst_x, dst_y);
1695 }
1696
1697 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1698 {
1699   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1700                       frame, tilesize);
1701   MarkTileDirty(x / tilesize, y / tilesize);
1702 }
1703
1704 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1705                               int tilesize)
1706 {
1707   DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1708                               graphic, frame, tilesize);
1709   MarkTileDirty(x / tilesize, y / tilesize);
1710 }
1711
1712 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1713                          int tilesize)
1714 {
1715   Bitmap *src_bitmap;
1716   int src_x, src_y;
1717
1718   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1719   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1720 }
1721
1722 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1723                                  int frame, int tilesize)
1724 {
1725   Bitmap *src_bitmap;
1726   int src_x, src_y;
1727
1728   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1729   BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1730 }
1731
1732 void DrawMiniGraphic(int x, int y, int graphic)
1733 {
1734   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1735   MarkTileDirty(x / 2, y / 2);
1736 }
1737
1738 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1739 {
1740   Bitmap *src_bitmap;
1741   int src_x, src_y;
1742
1743   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1744   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1745 }
1746
1747 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1748                                             int graphic, int frame,
1749                                             int cut_mode, int mask_mode)
1750 {
1751   Bitmap *src_bitmap;
1752   int src_x, src_y;
1753   int dst_x, dst_y;
1754   int width = TILEX, height = TILEY;
1755   int cx = 0, cy = 0;
1756
1757   if (dx || dy)                 /* shifted graphic */
1758   {
1759     if (x < BX1)                /* object enters playfield from the left */
1760     {
1761       x = BX1;
1762       width = dx;
1763       cx = TILEX - dx;
1764       dx = 0;
1765     }
1766     else if (x > BX2)           /* object enters playfield from the right */
1767     {
1768       x = BX2;
1769       width = -dx;
1770       dx = TILEX + dx;
1771     }
1772     else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1773     {
1774       width += dx;
1775       cx = -dx;
1776       dx = 0;
1777     }
1778     else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1779       width -= dx;
1780     else if (dx)                /* general horizontal movement */
1781       MarkTileDirty(x + SIGN(dx), y);
1782
1783     if (y < BY1)                /* object enters playfield from the top */
1784     {
1785       if (cut_mode == CUT_BELOW) /* object completely above top border */
1786         return;
1787
1788       y = BY1;
1789       height = dy;
1790       cy = TILEY - dy;
1791       dy = 0;
1792     }
1793     else if (y > BY2)           /* object enters playfield from the bottom */
1794     {
1795       y = BY2;
1796       height = -dy;
1797       dy = TILEY + dy;
1798     }
1799     else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1800     {
1801       height += dy;
1802       cy = -dy;
1803       dy = 0;
1804     }
1805     else if (dy > 0 && cut_mode == CUT_ABOVE)
1806     {
1807       if (y == BY2)             /* object completely above bottom border */
1808         return;
1809
1810       height = dy;
1811       cy = TILEY - dy;
1812       dy = TILEY;
1813       MarkTileDirty(x, y + 1);
1814     }                           /* object leaves playfield to the bottom */
1815     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1816       height -= dy;
1817     else if (dy)                /* general vertical movement */
1818       MarkTileDirty(x, y + SIGN(dy));
1819   }
1820
1821 #if DEBUG
1822   if (!IN_SCR_FIELD(x, y))
1823   {
1824     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1825     printf("DrawGraphicShifted(): This should never happen!\n");
1826     return;
1827   }
1828 #endif
1829
1830   width = width * TILESIZE_VAR / TILESIZE;
1831   height = height * TILESIZE_VAR / TILESIZE;
1832   cx = cx * TILESIZE_VAR / TILESIZE;
1833   cy = cy * TILESIZE_VAR / TILESIZE;
1834   dx = dx * TILESIZE_VAR / TILESIZE;
1835   dy = dy * TILESIZE_VAR / TILESIZE;
1836
1837   if (width > 0 && height > 0)
1838   {
1839     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1840
1841     src_x += cx;
1842     src_y += cy;
1843
1844     dst_x = FX + x * TILEX_VAR + dx;
1845     dst_y = FY + y * TILEY_VAR + dy;
1846
1847     if (mask_mode == USE_MASKING)
1848       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1849                        dst_x, dst_y);
1850     else
1851       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1852                  dst_x, dst_y);
1853
1854     MarkTileDirty(x, y);
1855   }
1856 }
1857
1858 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1859                                             int graphic, int frame,
1860                                             int cut_mode, int mask_mode)
1861 {
1862   Bitmap *src_bitmap;
1863   int src_x, src_y;
1864   int dst_x, dst_y;
1865   int width = TILEX_VAR, height = TILEY_VAR;
1866   int x1 = x;
1867   int y1 = y;
1868   int x2 = x + SIGN(dx);
1869   int y2 = y + SIGN(dy);
1870
1871   /* movement with two-tile animations must be sync'ed with movement position,
1872      not with current GfxFrame (which can be higher when using slow movement) */
1873   int anim_pos = (dx ? ABS(dx) : ABS(dy));
1874   int anim_frames = graphic_info[graphic].anim_frames;
1875
1876   /* (we also need anim_delay here for movement animations with less frames) */
1877   int anim_delay = graphic_info[graphic].anim_delay;
1878   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1879
1880   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    /* only for falling! */
1881   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    /* only for falling! */
1882
1883   /* re-calculate animation frame for two-tile movement animation */
1884   frame = getGraphicAnimationFrame(graphic, sync_frame);
1885
1886   /* check if movement start graphic inside screen area and should be drawn */
1887   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1888   {
1889     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1890
1891     dst_x = FX + x1 * TILEX_VAR;
1892     dst_y = FY + y1 * TILEY_VAR;
1893
1894     if (mask_mode == USE_MASKING)
1895       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1896                        dst_x, dst_y);
1897     else
1898       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1899                  dst_x, dst_y);
1900
1901     MarkTileDirty(x1, y1);
1902   }
1903
1904   /* check if movement end graphic inside screen area and should be drawn */
1905   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1906   {
1907     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1908
1909     dst_x = FX + x2 * TILEX_VAR;
1910     dst_y = FY + y2 * TILEY_VAR;
1911
1912     if (mask_mode == USE_MASKING)
1913       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1914                        dst_x, dst_y);
1915     else
1916       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1917                  dst_x, dst_y);
1918
1919     MarkTileDirty(x2, y2);
1920   }
1921 }
1922
1923 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1924                                int graphic, int frame,
1925                                int cut_mode, int mask_mode)
1926 {
1927   if (graphic < 0)
1928   {
1929     DrawGraphic(x, y, graphic, frame);
1930
1931     return;
1932   }
1933
1934   if (graphic_info[graphic].double_movement)    /* EM style movement images */
1935     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1936   else
1937     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1938 }
1939
1940 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1941                                 int frame, int cut_mode)
1942 {
1943   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1944 }
1945
1946 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1947                           int cut_mode, int mask_mode)
1948 {
1949   int lx = LEVELX(x), ly = LEVELY(y);
1950   int graphic;
1951   int frame;
1952
1953   if (IN_LEV_FIELD(lx, ly))
1954   {
1955     SetRandomAnimationValue(lx, ly);
1956
1957     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1958     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1959
1960     /* do not use double (EM style) movement graphic when not moving */
1961     if (graphic_info[graphic].double_movement && !dx && !dy)
1962     {
1963       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1964       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1965     }
1966   }
1967   else  /* border element */
1968   {
1969     graphic = el2img(element);
1970     frame = getGraphicAnimationFrame(graphic, -1);
1971   }
1972
1973   if (element == EL_EXPANDABLE_WALL)
1974   {
1975     boolean left_stopped = FALSE, right_stopped = FALSE;
1976
1977     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1978       left_stopped = TRUE;
1979     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1980       right_stopped = TRUE;
1981
1982     if (left_stopped && right_stopped)
1983       graphic = IMG_WALL;
1984     else if (left_stopped)
1985     {
1986       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1987       frame = graphic_info[graphic].anim_frames - 1;
1988     }
1989     else if (right_stopped)
1990     {
1991       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1992       frame = graphic_info[graphic].anim_frames - 1;
1993     }
1994   }
1995
1996   if (dx || dy)
1997     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1998   else if (mask_mode == USE_MASKING)
1999     DrawGraphicThruMask(x, y, graphic, frame);
2000   else
2001     DrawGraphic(x, y, graphic, frame);
2002 }
2003
2004 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2005                          int cut_mode, int mask_mode)
2006 {
2007   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2008     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2009                          cut_mode, mask_mode);
2010 }
2011
2012 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2013                               int cut_mode)
2014 {
2015   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2016 }
2017
2018 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2019                              int cut_mode)
2020 {
2021   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2022 }
2023
2024 void DrawLevelElementThruMask(int x, int y, int element)
2025 {
2026   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2027 }
2028
2029 void DrawLevelFieldThruMask(int x, int y)
2030 {
2031   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2032 }
2033
2034 /* !!! implementation of quicksand is totally broken !!! */
2035 #define IS_CRUMBLED_TILE(x, y, e)                                       \
2036         (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
2037                              !IS_MOVING(x, y) ||                        \
2038                              (e) == EL_QUICKSAND_EMPTYING ||            \
2039                              (e) == EL_QUICKSAND_FAST_EMPTYING))
2040
2041 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2042                                                int graphic)
2043 {
2044   Bitmap *src_bitmap;
2045   int src_x, src_y;
2046   int width, height, cx, cy;
2047   int sx = SCREENX(x), sy = SCREENY(y);
2048   int crumbled_border_size = graphic_info[graphic].border_size;
2049   int crumbled_tile_size = graphic_info[graphic].tile_size;
2050   int crumbled_border_size_var =
2051     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2052   int i;
2053
2054   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2055
2056   for (i = 1; i < 4; i++)
2057   {
2058     int dxx = (i & 1 ? dx : 0);
2059     int dyy = (i & 2 ? dy : 0);
2060     int xx = x + dxx;
2061     int yy = y + dyy;
2062     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2063                    BorderElement);
2064
2065     /* check if neighbour field is of same crumble type */
2066     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2067                     graphic_info[graphic].class ==
2068                     graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2069
2070     /* return if check prevents inner corner */
2071     if (same == (dxx == dx && dyy == dy))
2072       return;
2073   }
2074
2075   /* if we reach this point, we have an inner corner */
2076
2077   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2078
2079   width  = crumbled_border_size_var;
2080   height = crumbled_border_size_var;
2081   cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
2082   cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2083
2084   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2085              width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2086 }
2087
2088 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2089                                           int dir)
2090 {
2091   Bitmap *src_bitmap;
2092   int src_x, src_y;
2093   int width, height, bx, by, cx, cy;
2094   int sx = SCREENX(x), sy = SCREENY(y);
2095   int crumbled_border_size = graphic_info[graphic].border_size;
2096   int crumbled_tile_size = graphic_info[graphic].tile_size;
2097   int crumbled_border_size_var =
2098     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2099   int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2100   int i;
2101
2102   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2103
2104   /* draw simple, sloppy, non-corner-accurate crumbled border */
2105
2106   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2107   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2108   cx = (dir == 2 ? crumbled_border_pos_var : 0);
2109   cy = (dir == 3 ? crumbled_border_pos_var : 0);
2110
2111   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2112              FX + sx * TILEX_VAR + cx,
2113              FY + sy * TILEY_VAR + cy);
2114
2115   /* (remaining middle border part must be at least as big as corner part) */
2116   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2117       crumbled_border_size_var >= TILESIZE_VAR / 3)
2118     return;
2119
2120   /* correct corners of crumbled border, if needed */
2121
2122   for (i = -1; i <= 1; i += 2)
2123   {
2124     int xx = x + (dir == 0 || dir == 3 ? i : 0);
2125     int yy = y + (dir == 1 || dir == 2 ? i : 0);
2126     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2127                    BorderElement);
2128
2129     /* check if neighbour field is of same crumble type */
2130     if (IS_CRUMBLED_TILE(xx, yy, element) &&
2131         graphic_info[graphic].class ==
2132         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2133     {
2134       /* no crumbled corner, but continued crumbled border */
2135
2136       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2137       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2138       int b1 = (i == 1 ? crumbled_border_size_var :
2139                 TILESIZE_VAR - 2 * crumbled_border_size_var);
2140
2141       width  = crumbled_border_size_var;
2142       height = crumbled_border_size_var;
2143
2144       if (dir == 1 || dir == 2)
2145       {
2146         cx = c1;
2147         cy = c2;
2148         bx = cx;
2149         by = b1;
2150       }
2151       else
2152       {
2153         cx = c2;
2154         cy = c1;
2155         bx = b1;
2156         by = cy;
2157       }
2158
2159       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2160                  width, height,
2161                  FX + sx * TILEX_VAR + cx,
2162                  FY + sy * TILEY_VAR + cy);
2163     }
2164   }
2165 }
2166
2167 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2168 {
2169   int sx = SCREENX(x), sy = SCREENY(y);
2170   int element;
2171   int i;
2172   static int xy[4][2] =
2173   {
2174     { 0, -1 },
2175     { -1, 0 },
2176     { +1, 0 },
2177     { 0, +1 }
2178   };
2179
2180   if (!IN_LEV_FIELD(x, y))
2181     return;
2182
2183   element = TILE_GFX_ELEMENT(x, y);
2184
2185   if (IS_CRUMBLED_TILE(x, y, element))          /* crumble field itself */
2186   {
2187     if (!IN_SCR_FIELD(sx, sy))
2188       return;
2189
2190     /* crumble field borders towards direct neighbour fields */
2191     for (i = 0; i < 4; i++)
2192     {
2193       int xx = x + xy[i][0];
2194       int yy = y + xy[i][1];
2195
2196       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2197                  BorderElement);
2198
2199       /* check if neighbour field is of same crumble type */
2200       if (IS_CRUMBLED_TILE(xx, yy, element) &&
2201           graphic_info[graphic].class ==
2202           graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2203         continue;
2204
2205       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2206     }
2207
2208     /* crumble inner field corners towards corner neighbour fields */
2209     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2210         graphic_info[graphic].anim_frames == 2)
2211     {
2212       for (i = 0; i < 4; i++)
2213       {
2214         int dx = (i & 1 ? +1 : -1);
2215         int dy = (i & 2 ? +1 : -1);
2216
2217         DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2218       }
2219     }
2220
2221     MarkTileDirty(sx, sy);
2222   }
2223   else          /* center field is not crumbled -- crumble neighbour fields */
2224   {
2225     /* crumble field borders of direct neighbour fields */
2226     for (i = 0; i < 4; i++)
2227     {
2228       int xx = x + xy[i][0];
2229       int yy = y + xy[i][1];
2230       int sxx = sx + xy[i][0];
2231       int syy = sy + xy[i][1];
2232
2233       if (!IN_LEV_FIELD(xx, yy) ||
2234           !IN_SCR_FIELD(sxx, syy))
2235         continue;
2236
2237       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2238         continue;
2239
2240       element = TILE_GFX_ELEMENT(xx, yy);
2241
2242       if (!IS_CRUMBLED_TILE(xx, yy, element))
2243         continue;
2244
2245       graphic = el_act2crm(element, ACTION_DEFAULT);
2246
2247       DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2248
2249       MarkTileDirty(sxx, syy);
2250     }
2251
2252     /* crumble inner field corners of corner neighbour fields */
2253     for (i = 0; i < 4; i++)
2254     {
2255       int dx = (i & 1 ? +1 : -1);
2256       int dy = (i & 2 ? +1 : -1);
2257       int xx = x + dx;
2258       int yy = y + dy;
2259       int sxx = sx + dx;
2260       int syy = sy + dy;
2261
2262       if (!IN_LEV_FIELD(xx, yy) ||
2263           !IN_SCR_FIELD(sxx, syy))
2264         continue;
2265
2266       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2267         continue;
2268
2269       element = TILE_GFX_ELEMENT(xx, yy);
2270
2271       if (!IS_CRUMBLED_TILE(xx, yy, element))
2272         continue;
2273
2274       graphic = el_act2crm(element, ACTION_DEFAULT);
2275
2276       if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2277           graphic_info[graphic].anim_frames == 2)
2278         DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2279
2280       MarkTileDirty(sxx, syy);
2281     }
2282   }
2283 }
2284
2285 void DrawLevelFieldCrumbled(int x, int y)
2286 {
2287   int graphic;
2288
2289   if (!IN_LEV_FIELD(x, y))
2290     return;
2291
2292   if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2293       GfxElement[x][y] != EL_UNDEFINED &&
2294       GFX_CRUMBLED(GfxElement[x][y]))
2295   {
2296     DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2297
2298     return;
2299   }
2300
2301   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2302
2303   DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2304 }
2305
2306 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2307                                    int step_frame)
2308 {
2309   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2310   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2311   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2312   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2313   int sx = SCREENX(x), sy = SCREENY(y);
2314
2315   DrawGraphic(sx, sy, graphic1, frame1);
2316   DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2317 }
2318
2319 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2320 {
2321   int sx = SCREENX(x), sy = SCREENY(y);
2322   static int xy[4][2] =
2323   {
2324     { 0, -1 },
2325     { -1, 0 },
2326     { +1, 0 },
2327     { 0, +1 }
2328   };
2329   int i;
2330
2331   /* crumble direct neighbour fields (required for field borders) */
2332   for (i = 0; i < 4; i++)
2333   {
2334     int xx = x + xy[i][0];
2335     int yy = y + xy[i][1];
2336     int sxx = sx + xy[i][0];
2337     int syy = sy + xy[i][1];
2338
2339     if (!IN_LEV_FIELD(xx, yy) ||
2340         !IN_SCR_FIELD(sxx, syy) ||
2341         !GFX_CRUMBLED(Feld[xx][yy]) ||
2342         IS_MOVING(xx, yy))
2343       continue;
2344
2345     DrawLevelField(xx, yy);
2346   }
2347
2348   /* crumble corner neighbour fields (required for inner field corners) */
2349   for (i = 0; i < 4; i++)
2350   {
2351     int dx = (i & 1 ? +1 : -1);
2352     int dy = (i & 2 ? +1 : -1);
2353     int xx = x + dx;
2354     int yy = y + dy;
2355     int sxx = sx + dx;
2356     int syy = sy + dy;
2357
2358     if (!IN_LEV_FIELD(xx, yy) ||
2359         !IN_SCR_FIELD(sxx, syy) ||
2360         !GFX_CRUMBLED(Feld[xx][yy]) ||
2361         IS_MOVING(xx, yy))
2362       continue;
2363
2364     int element = TILE_GFX_ELEMENT(xx, yy);
2365     int graphic = el_act2crm(element, ACTION_DEFAULT);
2366
2367     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2368         graphic_info[graphic].anim_frames == 2)
2369       DrawLevelField(xx, yy);
2370   }
2371 }
2372
2373 static int getBorderElement(int x, int y)
2374 {
2375   int border[7][2] =
2376   {
2377     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
2378     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
2379     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
2380     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2381     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
2382     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
2383     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
2384   };
2385   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2386   int steel_position = (x == -1         && y == -1              ? 0 :
2387                         x == lev_fieldx && y == -1              ? 1 :
2388                         x == -1         && y == lev_fieldy      ? 2 :
2389                         x == lev_fieldx && y == lev_fieldy      ? 3 :
2390                         x == -1         || x == lev_fieldx      ? 4 :
2391                         y == -1         || y == lev_fieldy      ? 5 : 6);
2392
2393   return border[steel_position][steel_type];
2394 }
2395
2396 void DrawScreenElement(int x, int y, int element)
2397 {
2398   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2399   DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2400 }
2401
2402 void DrawLevelElement(int x, int y, int element)
2403 {
2404   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2405     DrawScreenElement(SCREENX(x), SCREENY(y), element);
2406 }
2407
2408 void DrawScreenField(int x, int y)
2409 {
2410   int lx = LEVELX(x), ly = LEVELY(y);
2411   int element, content;
2412
2413   if (!IN_LEV_FIELD(lx, ly))
2414   {
2415     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2416       element = EL_EMPTY;
2417     else
2418       element = getBorderElement(lx, ly);
2419
2420     DrawScreenElement(x, y, element);
2421
2422     return;
2423   }
2424
2425   element = Feld[lx][ly];
2426   content = Store[lx][ly];
2427
2428   if (IS_MOVING(lx, ly))
2429   {
2430     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2431     boolean cut_mode = NO_CUTTING;
2432
2433     if (element == EL_QUICKSAND_EMPTYING ||
2434         element == EL_QUICKSAND_FAST_EMPTYING ||
2435         element == EL_MAGIC_WALL_EMPTYING ||
2436         element == EL_BD_MAGIC_WALL_EMPTYING ||
2437         element == EL_DC_MAGIC_WALL_EMPTYING ||
2438         element == EL_AMOEBA_DROPPING)
2439       cut_mode = CUT_ABOVE;
2440     else if (element == EL_QUICKSAND_FILLING ||
2441              element == EL_QUICKSAND_FAST_FILLING ||
2442              element == EL_MAGIC_WALL_FILLING ||
2443              element == EL_BD_MAGIC_WALL_FILLING ||
2444              element == EL_DC_MAGIC_WALL_FILLING)
2445       cut_mode = CUT_BELOW;
2446
2447     if (cut_mode == CUT_ABOVE)
2448       DrawScreenElement(x, y, element);
2449     else
2450       DrawScreenElement(x, y, EL_EMPTY);
2451
2452     if (horiz_move)
2453       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2454     else if (cut_mode == NO_CUTTING)
2455       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2456     else
2457     {
2458       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2459
2460       if (cut_mode == CUT_BELOW &&
2461           IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2462         DrawLevelElement(lx, ly + 1, element);
2463     }
2464
2465     if (content == EL_ACID)
2466     {
2467       int dir = MovDir[lx][ly];
2468       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2469       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2470
2471       DrawLevelElementThruMask(newlx, newly, EL_ACID);
2472
2473       // prevent target field from being drawn again (but without masking)
2474       // (this would happen if target field is scanned after moving element)
2475       Stop[newlx][newly] = TRUE;
2476     }
2477   }
2478   else if (IS_BLOCKED(lx, ly))
2479   {
2480     int oldx, oldy;
2481     int sx, sy;
2482     int horiz_move;
2483     boolean cut_mode = NO_CUTTING;
2484     int element_old, content_old;
2485
2486     Blocked2Moving(lx, ly, &oldx, &oldy);
2487     sx = SCREENX(oldx);
2488     sy = SCREENY(oldy);
2489     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2490                   MovDir[oldx][oldy] == MV_RIGHT);
2491
2492     element_old = Feld[oldx][oldy];
2493     content_old = Store[oldx][oldy];
2494
2495     if (element_old == EL_QUICKSAND_EMPTYING ||
2496         element_old == EL_QUICKSAND_FAST_EMPTYING ||
2497         element_old == EL_MAGIC_WALL_EMPTYING ||
2498         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2499         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2500         element_old == EL_AMOEBA_DROPPING)
2501       cut_mode = CUT_ABOVE;
2502
2503     DrawScreenElement(x, y, EL_EMPTY);
2504
2505     if (horiz_move)
2506       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2507                                NO_CUTTING);
2508     else if (cut_mode == NO_CUTTING)
2509       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2510                                cut_mode);
2511     else
2512       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2513                                cut_mode);
2514   }
2515   else if (IS_DRAWABLE(element))
2516     DrawScreenElement(x, y, element);
2517   else
2518     DrawScreenElement(x, y, EL_EMPTY);
2519 }
2520
2521 void DrawLevelField(int x, int y)
2522 {
2523   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2524     DrawScreenField(SCREENX(x), SCREENY(y));
2525   else if (IS_MOVING(x, y))
2526   {
2527     int newx,newy;
2528
2529     Moving2Blocked(x, y, &newx, &newy);
2530     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2531       DrawScreenField(SCREENX(newx), SCREENY(newy));
2532   }
2533   else if (IS_BLOCKED(x, y))
2534   {
2535     int oldx, oldy;
2536
2537     Blocked2Moving(x, y, &oldx, &oldy);
2538     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2539       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2540   }
2541 }
2542
2543 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2544                                 int (*el2img_function)(int), boolean masked,
2545                                 int element_bits_draw)
2546 {
2547   int element_base = map_mm_wall_element(element);
2548   int element_bits = (IS_DF_WALL(element) ?
2549                       element - EL_DF_WALL_START :
2550                       IS_MM_WALL(element) ?
2551                       element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2552   int graphic = el2img_function(element_base);
2553   int tilesize_draw = tilesize / 2;
2554   Bitmap *src_bitmap;
2555   int src_x, src_y;
2556   int i;
2557
2558   getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2559
2560   for (i = 0; i < 4; i++)
2561   {
2562     int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2563     int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2564
2565     if (!(element_bits_draw & (1 << i)))
2566       continue;
2567
2568     if (element_bits & (1 << i))
2569     {
2570       if (masked)
2571         BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2572                          tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2573       else
2574         BlitBitmap(src_bitmap, drawto, src_x, src_y,
2575                    tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2576     }
2577     else
2578     {
2579       if (!masked)
2580         ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2581                        tilesize_draw, tilesize_draw);
2582     }
2583   }
2584 }
2585
2586 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2587                            boolean masked, int element_bits_draw)
2588 {
2589   DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2590                       element, tilesize, el2edimg, masked, element_bits_draw);
2591 }
2592
2593 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2594                       int (*el2img_function)(int))
2595 {
2596   DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2597                       0x000f);
2598 }
2599
2600 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2601                          boolean masked)
2602 {
2603   if (IS_MM_WALL(element))
2604   {
2605     DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2606                         element, tilesize, el2edimg, masked, 0x000f);
2607   }
2608   else
2609   {
2610     int graphic = el2edimg(element);
2611
2612     if (masked)
2613       DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2614     else
2615       DrawSizedGraphic(x, y, graphic, 0, tilesize);
2616   }
2617 }
2618
2619 void DrawSizedElement(int x, int y, int element, int tilesize)
2620 {
2621   DrawSizedElementExt(x, y, element, tilesize, FALSE);
2622 }
2623
2624 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2625 {
2626   DrawSizedElementExt(x, y, element, tilesize, TRUE);
2627 }
2628
2629 void DrawMiniElement(int x, int y, int element)
2630 {
2631   int graphic;
2632
2633   graphic = el2edimg(element);
2634   DrawMiniGraphic(x, y, graphic);
2635 }
2636
2637 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2638                             int tilesize)
2639 {
2640   int x = sx + scroll_x, y = sy + scroll_y;
2641
2642   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2643     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2644   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2645     DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2646   else
2647     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2648 }
2649
2650 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2651 {
2652   int x = sx + scroll_x, y = sy + scroll_y;
2653
2654   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2655     DrawMiniElement(sx, sy, EL_EMPTY);
2656   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2657     DrawMiniElement(sx, sy, Feld[x][y]);
2658   else
2659     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2660 }
2661
2662 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2663                                  int x, int y, int xsize, int ysize,
2664                                  int tile_width, int tile_height)
2665 {
2666   Bitmap *src_bitmap;
2667   int src_x, src_y;
2668   int dst_x = startx + x * tile_width;
2669   int dst_y = starty + y * tile_height;
2670   int width  = graphic_info[graphic].width;
2671   int height = graphic_info[graphic].height;
2672   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2673   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2674   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2675   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2676   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2677   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2678   boolean draw_masked = graphic_info[graphic].draw_masked;
2679
2680   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2681
2682   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2683   {
2684     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2685     return;
2686   }
2687
2688   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2689             inner_sx + (x - 1) * tile_width  % inner_width);
2690   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2691             inner_sy + (y - 1) * tile_height % inner_height);
2692
2693   if (draw_masked)
2694     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2695                      dst_x, dst_y);
2696   else
2697     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2698                dst_x, dst_y);
2699 }
2700
2701 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2702                             int x, int y, int xsize, int ysize, int font_nr)
2703 {
2704   int font_width  = getFontWidth(font_nr);
2705   int font_height = getFontHeight(font_nr);
2706
2707   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2708                               font_width, font_height);
2709 }
2710
2711 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2712 {
2713   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2714   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2715   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2716   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2717   boolean no_delay = (tape.warp_forward);
2718   unsigned int anim_delay = 0;
2719   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2720   int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2721   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2722   int font_width = getFontWidth(font_nr);
2723   int font_height = getFontHeight(font_nr);
2724   int max_xsize = level.envelope[envelope_nr].xsize;
2725   int max_ysize = level.envelope[envelope_nr].ysize;
2726   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2727   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2728   int xend = max_xsize;
2729   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2730   int xstep = (xstart < xend ? 1 : 0);
2731   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2732   int start = 0;
2733   int end = MAX(xend - xstart, yend - ystart);
2734   int i;
2735
2736   for (i = start; i <= end; i++)
2737   {
2738     int last_frame = end;       // last frame of this "for" loop
2739     int x = xstart + i * xstep;
2740     int y = ystart + i * ystep;
2741     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2742     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2743     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2744     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2745     int xx, yy;
2746
2747     SetDrawtoField(DRAW_TO_FIELDBUFFER);
2748
2749     BlitScreenToBitmap(backbuffer);
2750
2751     SetDrawtoField(DRAW_TO_BACKBUFFER);
2752
2753     for (yy = 0; yy < ysize; yy++)
2754       for (xx = 0; xx < xsize; xx++)
2755         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2756
2757     DrawTextBuffer(sx + font_width, sy + font_height,
2758                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2759                    xsize - 2, ysize - 2, 0, mask_mode,
2760                    level.envelope[envelope_nr].autowrap,
2761                    level.envelope[envelope_nr].centered, FALSE);
2762
2763     redraw_mask |= REDRAW_FIELD;
2764     BackToFront();
2765
2766     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2767   }
2768 }
2769
2770 void ShowEnvelope(int envelope_nr)
2771 {
2772   int element = EL_ENVELOPE_1 + envelope_nr;
2773   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2774   int sound_opening = element_info[element].sound[ACTION_OPENING];
2775   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2776   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2777   boolean no_delay = (tape.warp_forward);
2778   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2779   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2780   int anim_mode = graphic_info[graphic].anim_mode;
2781   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2782                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2783
2784   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2785
2786   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2787
2788   if (anim_mode == ANIM_DEFAULT)
2789     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2790
2791   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2792
2793   if (tape.playing)
2794     Delay(wait_delay_value);
2795   else
2796     WaitForEventToContinue();
2797
2798   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2799
2800   if (anim_mode != ANIM_NONE)
2801     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2802
2803   if (anim_mode == ANIM_DEFAULT)
2804     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2805
2806   game.envelope_active = FALSE;
2807
2808   SetDrawtoField(DRAW_TO_FIELDBUFFER);
2809
2810   redraw_mask |= REDRAW_FIELD;
2811   BackToFront();
2812 }
2813
2814 static void setRequestBasePosition(int *x, int *y)
2815 {
2816   int sx_base, sy_base;
2817
2818   if (request.x != -1)
2819     sx_base = request.x;
2820   else if (request.align == ALIGN_LEFT)
2821     sx_base = SX;
2822   else if (request.align == ALIGN_RIGHT)
2823     sx_base = SX + SXSIZE;
2824   else
2825     sx_base = SX + SXSIZE / 2;
2826
2827   if (request.y != -1)
2828     sy_base = request.y;
2829   else if (request.valign == VALIGN_TOP)
2830     sy_base = SY;
2831   else if (request.valign == VALIGN_BOTTOM)
2832     sy_base = SY + SYSIZE;
2833   else
2834     sy_base = SY + SYSIZE / 2;
2835
2836   *x = sx_base;
2837   *y = sy_base;
2838 }
2839
2840 static void setRequestPositionExt(int *x, int *y, int width, int height,
2841                                   boolean add_border_size)
2842 {
2843   int border_size = request.border_size;
2844   int sx_base, sy_base;
2845   int sx, sy;
2846
2847   setRequestBasePosition(&sx_base, &sy_base);
2848
2849   if (request.align == ALIGN_LEFT)
2850     sx = sx_base;
2851   else if (request.align == ALIGN_RIGHT)
2852     sx = sx_base - width;
2853   else
2854     sx = sx_base - width  / 2;
2855
2856   if (request.valign == VALIGN_TOP)
2857     sy = sy_base;
2858   else if (request.valign == VALIGN_BOTTOM)
2859     sy = sy_base - height;
2860   else
2861     sy = sy_base - height / 2;
2862
2863   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2864   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2865
2866   if (add_border_size)
2867   {
2868     sx += border_size;
2869     sy += border_size;
2870   }
2871
2872   *x = sx;
2873   *y = sy;
2874 }
2875
2876 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2877 {
2878   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2879 }
2880
2881 void DrawEnvelopeRequest(char *text)
2882 {
2883   char *text_final = text;
2884   char *text_door_style = NULL;
2885   int graphic = IMG_BACKGROUND_REQUEST;
2886   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2887   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2888   int font_nr = FONT_REQUEST;
2889   int font_width = getFontWidth(font_nr);
2890   int font_height = getFontHeight(font_nr);
2891   int border_size = request.border_size;
2892   int line_spacing = request.line_spacing;
2893   int line_height = font_height + line_spacing;
2894   int max_text_width  = request.width  - 2 * border_size;
2895   int max_text_height = request.height - 2 * border_size;
2896   int line_length = max_text_width  / font_width;
2897   int max_lines   = max_text_height / line_height;
2898   int text_width = line_length * font_width;
2899   int width = request.width;
2900   int height = request.height;
2901   int tile_size = MAX(request.step_offset, 1);
2902   int x_steps = width  / tile_size;
2903   int y_steps = height / tile_size;
2904   int sx_offset = border_size;
2905   int sy_offset = border_size;
2906   int sx, sy;
2907   int i, x, y;
2908
2909   if (request.centered)
2910     sx_offset = (request.width - text_width) / 2;
2911
2912   if (request.wrap_single_words && !request.autowrap)
2913   {
2914     char *src_text_ptr, *dst_text_ptr;
2915
2916     text_door_style = checked_malloc(2 * strlen(text) + 1);
2917
2918     src_text_ptr = text;
2919     dst_text_ptr = text_door_style;
2920
2921     while (*src_text_ptr)
2922     {
2923       if (*src_text_ptr == ' ' ||
2924           *src_text_ptr == '?' ||
2925           *src_text_ptr == '!')
2926         *dst_text_ptr++ = '\n';
2927
2928       if (*src_text_ptr != ' ')
2929         *dst_text_ptr++ = *src_text_ptr;
2930
2931       src_text_ptr++;
2932     }
2933
2934     *dst_text_ptr = '\0';
2935
2936     text_final = text_door_style;
2937   }
2938
2939   setRequestPosition(&sx, &sy, FALSE);
2940
2941   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2942
2943   for (y = 0; y < y_steps; y++)
2944     for (x = 0; x < x_steps; x++)
2945       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2946                                   x, y, x_steps, y_steps,
2947                                   tile_size, tile_size);
2948
2949   /* force DOOR font inside door area */
2950   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2951
2952   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2953                  line_length, -1, max_lines, line_spacing, mask_mode,
2954                  request.autowrap, request.centered, FALSE);
2955
2956   ResetFontStatus();
2957
2958   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2959     RedrawGadget(tool_gadget[i]);
2960
2961   // store readily prepared envelope request for later use when animating
2962   BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2963
2964   if (text_door_style)
2965     free(text_door_style);
2966 }
2967
2968 void AnimateEnvelopeRequest(int anim_mode, int action)
2969 {
2970   int graphic = IMG_BACKGROUND_REQUEST;
2971   boolean draw_masked = graphic_info[graphic].draw_masked;
2972   int delay_value_normal = request.step_delay;
2973   int delay_value_fast = delay_value_normal / 2;
2974   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2975   boolean no_delay = (tape.warp_forward);
2976   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2977   int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2978   unsigned int anim_delay = 0;
2979
2980   int tile_size = MAX(request.step_offset, 1);
2981   int max_xsize = request.width  / tile_size;
2982   int max_ysize = request.height / tile_size;
2983   int max_xsize_inner = max_xsize - 2;
2984   int max_ysize_inner = max_ysize - 2;
2985
2986   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2987   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2988   int xend = max_xsize_inner;
2989   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2990   int xstep = (xstart < xend ? 1 : 0);
2991   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2992   int start = 0;
2993   int end = MAX(xend - xstart, yend - ystart);
2994   int i;
2995
2996   if (setup.quick_doors)
2997   {
2998     xstart = xend;
2999     ystart = yend;
3000     end = 0;
3001   }
3002
3003   for (i = start; i <= end; i++)
3004   {
3005     int last_frame = end;       // last frame of this "for" loop
3006     int x = xstart + i * xstep;
3007     int y = ystart + i * ystep;
3008     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3009     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3010     int xsize_size_left = (xsize - 1) * tile_size;
3011     int ysize_size_top  = (ysize - 1) * tile_size;
3012     int max_xsize_pos = (max_xsize - 1) * tile_size;
3013     int max_ysize_pos = (max_ysize - 1) * tile_size;
3014     int width  = xsize * tile_size;
3015     int height = ysize * tile_size;
3016     int src_x, src_y;
3017     int dst_x, dst_y;
3018     int xx, yy;
3019
3020     setRequestPosition(&src_x, &src_y, FALSE);
3021     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3022
3023     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3024
3025     for (yy = 0; yy < 2; yy++)
3026     {
3027       for (xx = 0; xx < 2; xx++)
3028       {
3029         int src_xx = src_x + xx * max_xsize_pos;
3030         int src_yy = src_y + yy * max_ysize_pos;
3031         int dst_xx = dst_x + xx * xsize_size_left;
3032         int dst_yy = dst_y + yy * ysize_size_top;
3033         int xx_size = (xx ? tile_size : xsize_size_left);
3034         int yy_size = (yy ? tile_size : ysize_size_top);
3035
3036         if (draw_masked)
3037           BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3038                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3039         else
3040           BlitBitmap(bitmap_db_store_2, backbuffer,
3041                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3042       }
3043     }
3044
3045     redraw_mask |= REDRAW_FIELD;
3046
3047     BackToFront();
3048
3049     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3050   }
3051 }
3052
3053 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3054 {
3055   int graphic = IMG_BACKGROUND_REQUEST;
3056   int sound_opening = SND_REQUEST_OPENING;
3057   int sound_closing = SND_REQUEST_CLOSING;
3058   int anim_mode_1 = request.anim_mode;                  /* (higher priority) */
3059   int anim_mode_2 = graphic_info[graphic].anim_mode;    /* (lower priority) */
3060   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3061   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3062                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3063
3064   if (game_status == GAME_MODE_PLAYING)
3065     BlitScreenToBitmap(backbuffer);
3066
3067   SetDrawtoField(DRAW_TO_BACKBUFFER);
3068
3069   // SetDrawBackgroundMask(REDRAW_NONE);
3070
3071   if (action == ACTION_OPENING)
3072   {
3073     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3074
3075     if (req_state & REQ_ASK)
3076     {
3077       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3078       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3079     }
3080     else if (req_state & REQ_CONFIRM)
3081     {
3082       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3083     }
3084     else if (req_state & REQ_PLAYER)
3085     {
3086       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3087       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3088       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3089       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3090     }
3091
3092     DrawEnvelopeRequest(text);
3093   }
3094
3095   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
3096
3097   if (action == ACTION_OPENING)
3098   {
3099     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3100
3101     if (anim_mode == ANIM_DEFAULT)
3102       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3103
3104     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3105   }
3106   else
3107   {
3108     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3109
3110     if (anim_mode != ANIM_NONE)
3111       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3112
3113     if (anim_mode == ANIM_DEFAULT)
3114       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3115   }
3116
3117   game.envelope_active = FALSE;
3118
3119   if (action == ACTION_CLOSING)
3120     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3121
3122   // SetDrawBackgroundMask(last_draw_background_mask);
3123
3124   redraw_mask |= REDRAW_FIELD;
3125
3126   BackToFront();
3127
3128   if (action == ACTION_CLOSING &&
3129       game_status == GAME_MODE_PLAYING &&
3130       level.game_engine_type == GAME_ENGINE_TYPE_RND)
3131     SetDrawtoField(DRAW_TO_FIELDBUFFER);
3132 }
3133
3134 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3135 {
3136   if (IS_MM_WALL(element))
3137   {
3138     DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3139   }
3140   else
3141   {
3142     Bitmap *src_bitmap;
3143     int src_x, src_y;
3144     int graphic = el2preimg(element);
3145
3146     getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3147     BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3148                dst_x, dst_y);
3149   }
3150 }
3151
3152 void DrawLevel(int draw_background_mask)
3153 {
3154   int x,y;
3155
3156   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3157   SetDrawBackgroundMask(draw_background_mask);
3158
3159   ClearField();
3160
3161   for (x = BX1; x <= BX2; x++)
3162     for (y = BY1; y <= BY2; y++)
3163       DrawScreenField(x, y);
3164
3165   redraw_mask |= REDRAW_FIELD;
3166 }
3167
3168 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3169                     int tilesize)
3170 {
3171   int x,y;
3172
3173   for (x = 0; x < size_x; x++)
3174     for (y = 0; y < size_y; y++)
3175       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3176
3177   redraw_mask |= REDRAW_FIELD;
3178 }
3179
3180 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3181 {
3182   int x,y;
3183
3184   for (x = 0; x < size_x; x++)
3185     for (y = 0; y < size_y; y++)
3186       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3187
3188   redraw_mask |= REDRAW_FIELD;
3189 }
3190
3191 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3192 {
3193   boolean show_level_border = (BorderElement != EL_EMPTY);
3194   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3195   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3196   int tile_size = preview.tile_size;
3197   int preview_width  = preview.xsize * tile_size;
3198   int preview_height = preview.ysize * tile_size;
3199   int real_preview_xsize = MIN(level_xsize, preview.xsize);
3200   int real_preview_ysize = MIN(level_ysize, preview.ysize);
3201   int real_preview_width  = real_preview_xsize * tile_size;
3202   int real_preview_height = real_preview_ysize * tile_size;
3203   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3204   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3205   int x, y;
3206
3207   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3208     return;
3209
3210   DrawBackground(dst_x, dst_y, preview_width, preview_height);
3211
3212   dst_x += (preview_width  - real_preview_width)  / 2;
3213   dst_y += (preview_height - real_preview_height) / 2;
3214
3215   for (x = 0; x < real_preview_xsize; x++)
3216   {
3217     for (y = 0; y < real_preview_ysize; y++)
3218     {
3219       int lx = from_x + x + (show_level_border ? -1 : 0);
3220       int ly = from_y + y + (show_level_border ? -1 : 0);
3221       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3222                      getBorderElement(lx, ly));
3223
3224       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3225                          element, tile_size);
3226     }
3227   }
3228
3229   redraw_mask |= REDRAW_FIELD;
3230 }
3231
3232 #define MICROLABEL_EMPTY                0
3233 #define MICROLABEL_LEVEL_NAME           1
3234 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
3235 #define MICROLABEL_LEVEL_AUTHOR         3
3236 #define MICROLABEL_IMPORTED_FROM_HEAD   4
3237 #define MICROLABEL_IMPORTED_FROM        5
3238 #define MICROLABEL_IMPORTED_BY_HEAD     6
3239 #define MICROLABEL_IMPORTED_BY          7
3240
3241 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3242 {
3243   int max_text_width = SXSIZE;
3244   int font_width = getFontWidth(font_nr);
3245
3246   if (pos->align == ALIGN_CENTER)
3247     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3248   else if (pos->align == ALIGN_RIGHT)
3249     max_text_width = pos->x;
3250   else
3251     max_text_width = SXSIZE - pos->x;
3252
3253   return max_text_width / font_width;
3254 }
3255
3256 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3257 {
3258   char label_text[MAX_OUTPUT_LINESIZE + 1];
3259   int max_len_label_text;
3260   int font_nr = pos->font;
3261   int i;
3262
3263   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3264     return;
3265
3266   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3267       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3268       mode == MICROLABEL_IMPORTED_BY_HEAD)
3269     font_nr = pos->font_alt;
3270
3271   max_len_label_text = getMaxTextLength(pos, font_nr);
3272
3273   if (pos->size != -1)
3274     max_len_label_text = pos->size;
3275
3276   for (i = 0; i < max_len_label_text; i++)
3277     label_text[i] = ' ';
3278   label_text[max_len_label_text] = '\0';
3279
3280   if (strlen(label_text) > 0)
3281     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3282
3283   strncpy(label_text,
3284           (mode == MICROLABEL_LEVEL_NAME ? level.name :
3285            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3286            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3287            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3288            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3289            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3290            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3291           max_len_label_text);
3292   label_text[max_len_label_text] = '\0';
3293
3294   if (strlen(label_text) > 0)
3295     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3296
3297   redraw_mask |= REDRAW_FIELD;
3298 }
3299
3300 static void DrawPreviewLevelLabel(int mode)
3301 {
3302   DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3303 }
3304
3305 static void DrawPreviewLevelInfo(int mode)
3306 {
3307   if (mode == MICROLABEL_LEVEL_NAME)
3308     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3309   else if (mode == MICROLABEL_LEVEL_AUTHOR)
3310     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3311 }
3312
3313 static void DrawPreviewLevelExt(boolean restart)
3314 {
3315   static unsigned int scroll_delay = 0;
3316   static unsigned int label_delay = 0;
3317   static int from_x, from_y, scroll_direction;
3318   static int label_state, label_counter;
3319   unsigned int scroll_delay_value = preview.step_delay;
3320   boolean show_level_border = (BorderElement != EL_EMPTY);
3321   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3322   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3323
3324   if (restart)
3325   {
3326     from_x = 0;
3327     from_y = 0;
3328
3329     if (preview.anim_mode == ANIM_CENTERED)
3330     {
3331       if (level_xsize > preview.xsize)
3332         from_x = (level_xsize - preview.xsize) / 2;
3333       if (level_ysize > preview.ysize)
3334         from_y = (level_ysize - preview.ysize) / 2;
3335     }
3336
3337     from_x += preview.xoffset;
3338     from_y += preview.yoffset;
3339
3340     scroll_direction = MV_RIGHT;
3341     label_state = 1;
3342     label_counter = 0;
3343
3344     DrawPreviewLevelPlayfield(from_x, from_y);
3345     DrawPreviewLevelLabel(label_state);
3346
3347     DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3348     DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3349
3350     /* initialize delay counters */
3351     DelayReached(&scroll_delay, 0);
3352     DelayReached(&label_delay, 0);
3353
3354     if (leveldir_current->name)
3355     {
3356       struct TextPosInfo *pos = &menu.main.text.level_info_1;
3357       char label_text[MAX_OUTPUT_LINESIZE + 1];
3358       int font_nr = pos->font;
3359       int max_len_label_text = getMaxTextLength(pos, font_nr);
3360
3361       if (pos->size != -1)
3362         max_len_label_text = pos->size;
3363
3364       strncpy(label_text, leveldir_current->name, max_len_label_text);
3365       label_text[max_len_label_text] = '\0';
3366
3367       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3368         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3369     }
3370
3371     return;
3372   }
3373
3374   /* scroll preview level, if needed */
3375   if (preview.anim_mode != ANIM_NONE &&
3376       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3377       DelayReached(&scroll_delay, scroll_delay_value))
3378   {
3379     switch (scroll_direction)
3380     {
3381       case MV_LEFT:
3382         if (from_x > 0)
3383         {
3384           from_x -= preview.step_offset;
3385           from_x = (from_x < 0 ? 0 : from_x);
3386         }
3387         else
3388           scroll_direction = MV_UP;
3389         break;
3390
3391       case MV_RIGHT:
3392         if (from_x < level_xsize - preview.xsize)
3393         {
3394           from_x += preview.step_offset;
3395           from_x = (from_x > level_xsize - preview.xsize ?
3396                     level_xsize - preview.xsize : from_x);
3397         }
3398         else
3399           scroll_direction = MV_DOWN;
3400         break;
3401
3402       case MV_UP:
3403         if (from_y > 0)
3404         {
3405           from_y -= preview.step_offset;
3406           from_y = (from_y < 0 ? 0 : from_y);
3407         }
3408         else
3409           scroll_direction = MV_RIGHT;
3410         break;
3411
3412       case MV_DOWN:
3413         if (from_y < level_ysize - preview.ysize)
3414         {
3415           from_y += preview.step_offset;
3416           from_y = (from_y > level_ysize - preview.ysize ?
3417                     level_ysize - preview.ysize : from_y);
3418         }
3419         else
3420           scroll_direction = MV_LEFT;
3421         break;
3422
3423       default:
3424         break;
3425     }
3426
3427     DrawPreviewLevelPlayfield(from_x, from_y);
3428   }
3429
3430   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3431   /* redraw micro level label, if needed */
3432   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3433       !strEqual(level.author, ANONYMOUS_NAME) &&
3434       !strEqual(level.author, leveldir_current->name) &&
3435       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3436   {
3437     int max_label_counter = 23;
3438
3439     if (leveldir_current->imported_from != NULL &&
3440         strlen(leveldir_current->imported_from) > 0)
3441       max_label_counter += 14;
3442     if (leveldir_current->imported_by != NULL &&
3443         strlen(leveldir_current->imported_by) > 0)
3444       max_label_counter += 14;
3445
3446     label_counter = (label_counter + 1) % max_label_counter;
3447     label_state = (label_counter >= 0 && label_counter <= 7 ?
3448                    MICROLABEL_LEVEL_NAME :
3449                    label_counter >= 9 && label_counter <= 12 ?
3450                    MICROLABEL_LEVEL_AUTHOR_HEAD :
3451                    label_counter >= 14 && label_counter <= 21 ?
3452                    MICROLABEL_LEVEL_AUTHOR :
3453                    label_counter >= 23 && label_counter <= 26 ?
3454                    MICROLABEL_IMPORTED_FROM_HEAD :
3455                    label_counter >= 28 && label_counter <= 35 ?
3456                    MICROLABEL_IMPORTED_FROM :
3457                    label_counter >= 37 && label_counter <= 40 ?
3458                    MICROLABEL_IMPORTED_BY_HEAD :
3459                    label_counter >= 42 && label_counter <= 49 ?
3460                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3461
3462     if (leveldir_current->imported_from == NULL &&
3463         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3464          label_state == MICROLABEL_IMPORTED_FROM))
3465       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3466                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3467
3468     DrawPreviewLevelLabel(label_state);
3469   }
3470 }
3471
3472 void DrawPreviewLevelInitial()
3473 {
3474   DrawPreviewLevelExt(TRUE);
3475 }
3476
3477 void DrawPreviewLevelAnimation()
3478 {
3479   DrawPreviewLevelExt(FALSE);
3480 }
3481
3482 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3483                                            int graphic, int sync_frame,
3484                                            int mask_mode)
3485 {
3486   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3487
3488   if (mask_mode == USE_MASKING)
3489     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3490   else
3491     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3492 }
3493
3494 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3495                                   int graphic, int sync_frame, int mask_mode)
3496 {
3497   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3498
3499   if (mask_mode == USE_MASKING)
3500     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3501   else
3502     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3503 }
3504
3505 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3506 {
3507   int lx = LEVELX(x), ly = LEVELY(y);
3508
3509   if (!IN_SCR_FIELD(x, y))
3510     return;
3511
3512   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3513                           graphic, GfxFrame[lx][ly], NO_MASKING);
3514
3515   MarkTileDirty(x, y);
3516 }
3517
3518 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3519 {
3520   int lx = LEVELX(x), ly = LEVELY(y);
3521
3522   if (!IN_SCR_FIELD(x, y))
3523     return;
3524
3525   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3526                           graphic, GfxFrame[lx][ly], NO_MASKING);
3527   MarkTileDirty(x, y);
3528 }
3529
3530 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3531 {
3532   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3533 }
3534
3535 void DrawLevelElementAnimation(int x, int y, int element)
3536 {
3537   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3538
3539   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3540 }
3541
3542 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3543 {
3544   int sx = SCREENX(x), sy = SCREENY(y);
3545
3546   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3547     return;
3548
3549   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3550     return;
3551
3552   DrawGraphicAnimation(sx, sy, graphic);
3553
3554 #if 1
3555   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3556     DrawLevelFieldCrumbled(x, y);
3557 #else
3558   if (GFX_CRUMBLED(Feld[x][y]))
3559     DrawLevelFieldCrumbled(x, y);
3560 #endif
3561 }
3562
3563 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3564 {
3565   int sx = SCREENX(x), sy = SCREENY(y);
3566   int graphic;
3567
3568   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3569     return;
3570
3571   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3572
3573   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3574     return;
3575
3576   DrawGraphicAnimation(sx, sy, graphic);
3577
3578   if (GFX_CRUMBLED(element))
3579     DrawLevelFieldCrumbled(x, y);
3580 }
3581
3582 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3583 {
3584   if (player->use_murphy)
3585   {
3586     /* this works only because currently only one player can be "murphy" ... */
3587     static int last_horizontal_dir = MV_LEFT;
3588     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3589
3590     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3591       last_horizontal_dir = move_dir;
3592
3593     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
3594     {
3595       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3596
3597       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3598     }
3599
3600     return graphic;
3601   }
3602   else
3603     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3604 }
3605
3606 static boolean equalGraphics(int graphic1, int graphic2)
3607 {
3608   struct GraphicInfo *g1 = &graphic_info[graphic1];
3609   struct GraphicInfo *g2 = &graphic_info[graphic2];
3610
3611   return (g1->bitmap      == g2->bitmap &&
3612           g1->src_x       == g2->src_x &&
3613           g1->src_y       == g2->src_y &&
3614           g1->anim_frames == g2->anim_frames &&
3615           g1->anim_delay  == g2->anim_delay &&
3616           g1->anim_mode   == g2->anim_mode);
3617 }
3618
3619 void DrawAllPlayers()
3620 {
3621   int i;
3622
3623   for (i = 0; i < MAX_PLAYERS; i++)
3624     if (stored_player[i].active)
3625       DrawPlayer(&stored_player[i]);
3626 }
3627
3628 void DrawPlayerField(int x, int y)
3629 {
3630   if (!IS_PLAYER(x, y))
3631     return;
3632
3633   DrawPlayer(PLAYERINFO(x, y));
3634 }
3635
3636 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3637
3638 void DrawPlayer(struct PlayerInfo *player)
3639 {
3640   int jx = player->jx;
3641   int jy = player->jy;
3642   int move_dir = player->MovDir;
3643   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3644   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
3645   int last_jx = (player->is_moving ? jx - dx : jx);
3646   int last_jy = (player->is_moving ? jy - dy : jy);
3647   int next_jx = jx + dx;
3648   int next_jy = jy + dy;
3649   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3650   boolean player_is_opaque = FALSE;
3651   int sx = SCREENX(jx), sy = SCREENY(jy);
3652   int sxx = 0, syy = 0;
3653   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3654   int graphic;
3655   int action = ACTION_DEFAULT;
3656   int last_player_graphic = getPlayerGraphic(player, move_dir);
3657   int last_player_frame = player->Frame;
3658   int frame = 0;
3659
3660   /* GfxElement[][] is set to the element the player is digging or collecting;
3661      remove also for off-screen player if the player is not moving anymore */
3662   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3663     GfxElement[jx][jy] = EL_UNDEFINED;
3664
3665   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3666     return;
3667
3668 #if DEBUG
3669   if (!IN_LEV_FIELD(jx, jy))
3670   {
3671     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3672     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3673     printf("DrawPlayerField(): This should never happen!\n");
3674     return;
3675   }
3676 #endif
3677
3678   if (element == EL_EXPLOSION)
3679     return;
3680
3681   action = (player->is_pushing    ? ACTION_PUSHING         :
3682             player->is_digging    ? ACTION_DIGGING         :
3683             player->is_collecting ? ACTION_COLLECTING      :
3684             player->is_moving     ? ACTION_MOVING          :
3685             player->is_snapping   ? ACTION_SNAPPING        :
3686             player->is_dropping   ? ACTION_DROPPING        :
3687             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
3688
3689   if (player->is_waiting)
3690     move_dir = player->dir_waiting;
3691
3692   InitPlayerGfxAnimation(player, action, move_dir);
3693
3694   /* ----------------------------------------------------------------------- */
3695   /* draw things in the field the player is leaving, if needed               */
3696   /* ----------------------------------------------------------------------- */
3697
3698   if (player->is_moving)
3699   {
3700     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3701     {
3702       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3703
3704       if (last_element == EL_DYNAMITE_ACTIVE ||
3705           last_element == EL_EM_DYNAMITE_ACTIVE ||
3706           last_element == EL_SP_DISK_RED_ACTIVE)
3707         DrawDynamite(last_jx, last_jy);
3708       else
3709         DrawLevelFieldThruMask(last_jx, last_jy);
3710     }
3711     else if (last_element == EL_DYNAMITE_ACTIVE ||
3712              last_element == EL_EM_DYNAMITE_ACTIVE ||
3713              last_element == EL_SP_DISK_RED_ACTIVE)
3714       DrawDynamite(last_jx, last_jy);
3715 #if 0
3716     /* !!! this is not enough to prevent flickering of players which are
3717        moving next to each others without a free tile between them -- this
3718        can only be solved by drawing all players layer by layer (first the
3719        background, then the foreground etc.) !!! => TODO */
3720     else if (!IS_PLAYER(last_jx, last_jy))
3721       DrawLevelField(last_jx, last_jy);
3722 #else
3723     else
3724       DrawLevelField(last_jx, last_jy);
3725 #endif
3726
3727     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3728       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3729   }
3730
3731   if (!IN_SCR_FIELD(sx, sy))
3732     return;
3733
3734   /* ----------------------------------------------------------------------- */
3735   /* draw things behind the player, if needed                                */
3736   /* ----------------------------------------------------------------------- */
3737
3738   if (Back[jx][jy])
3739     DrawLevelElement(jx, jy, Back[jx][jy]);
3740   else if (IS_ACTIVE_BOMB(element))
3741     DrawLevelElement(jx, jy, EL_EMPTY);
3742   else
3743   {
3744     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3745     {
3746       int old_element = GfxElement[jx][jy];
3747       int old_graphic = el_act_dir2img(old_element, action, move_dir);
3748       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3749
3750       if (GFX_CRUMBLED(old_element))
3751         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3752       else
3753         DrawGraphic(sx, sy, old_graphic, frame);
3754
3755       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3756         player_is_opaque = TRUE;
3757     }
3758     else
3759     {
3760       GfxElement[jx][jy] = EL_UNDEFINED;
3761
3762       /* make sure that pushed elements are drawn with correct frame rate */
3763       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3764
3765       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3766         GfxFrame[jx][jy] = player->StepFrame;
3767
3768       DrawLevelField(jx, jy);
3769     }
3770   }
3771
3772 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3773   /* ----------------------------------------------------------------------- */
3774   /* draw player himself                                                     */
3775   /* ----------------------------------------------------------------------- */
3776
3777   graphic = getPlayerGraphic(player, move_dir);
3778
3779   /* in the case of changed player action or direction, prevent the current
3780      animation frame from being restarted for identical animations */
3781   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3782     player->Frame = last_player_frame;
3783
3784   frame = getGraphicAnimationFrame(graphic, player->Frame);
3785
3786   if (player->GfxPos)
3787   {
3788     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3789       sxx = player->GfxPos;
3790     else
3791       syy = player->GfxPos;
3792   }
3793
3794   if (player_is_opaque)
3795     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3796   else
3797     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3798
3799   if (SHIELD_ON(player))
3800   {
3801     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3802                    IMG_SHIELD_NORMAL_ACTIVE);
3803     int frame = getGraphicAnimationFrame(graphic, -1);
3804
3805     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3806   }
3807 #endif
3808
3809 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3810   if (player->GfxPos)
3811   {
3812     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3813       sxx = player->GfxPos;
3814     else
3815       syy = player->GfxPos;
3816   }
3817 #endif
3818
3819   /* ----------------------------------------------------------------------- */
3820   /* draw things the player is pushing, if needed                            */
3821   /* ----------------------------------------------------------------------- */
3822
3823   if (player->is_pushing && player->is_moving)
3824   {
3825     int px = SCREENX(jx), py = SCREENY(jy);
3826     int pxx = (TILEX - ABS(sxx)) * dx;
3827     int pyy = (TILEY - ABS(syy)) * dy;
3828     int gfx_frame = GfxFrame[jx][jy];
3829
3830     int graphic;
3831     int sync_frame;
3832     int frame;
3833
3834     if (!IS_MOVING(jx, jy))             /* push movement already finished */
3835     {
3836       element = Feld[next_jx][next_jy];
3837       gfx_frame = GfxFrame[next_jx][next_jy];
3838     }
3839
3840     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3841
3842     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3843     frame = getGraphicAnimationFrame(graphic, sync_frame);
3844
3845     /* draw background element under pushed element (like the Sokoban field) */
3846     if (game.use_masked_pushing && IS_MOVING(jx, jy))
3847     {
3848       /* this allows transparent pushing animation over non-black background */
3849
3850       if (Back[jx][jy])
3851         DrawLevelElement(jx, jy, Back[jx][jy]);
3852       else
3853         DrawLevelElement(jx, jy, EL_EMPTY);
3854
3855       if (Back[next_jx][next_jy])
3856         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3857       else
3858         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3859     }
3860     else if (Back[next_jx][next_jy])
3861       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3862
3863 #if 1
3864     /* do not draw (EM style) pushing animation when pushing is finished */
3865     /* (two-tile animations usually do not contain start and end frame) */
3866     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3867       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3868     else
3869       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3870 #else
3871     /* masked drawing is needed for EMC style (double) movement graphics */
3872     /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3873     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3874 #endif
3875   }
3876
3877 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3878   /* ----------------------------------------------------------------------- */
3879   /* draw player himself                                                     */
3880   /* ----------------------------------------------------------------------- */
3881
3882   graphic = getPlayerGraphic(player, move_dir);
3883
3884   /* in the case of changed player action or direction, prevent the current
3885      animation frame from being restarted for identical animations */
3886   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3887     player->Frame = last_player_frame;
3888
3889   frame = getGraphicAnimationFrame(graphic, player->Frame);
3890
3891   if (player->GfxPos)
3892   {
3893     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3894       sxx = player->GfxPos;
3895     else
3896       syy = player->GfxPos;
3897   }
3898
3899   if (player_is_opaque)
3900     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3901   else
3902     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3903
3904   if (SHIELD_ON(player))
3905   {
3906     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3907                    IMG_SHIELD_NORMAL_ACTIVE);
3908     int frame = getGraphicAnimationFrame(graphic, -1);
3909
3910     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3911   }
3912 #endif
3913
3914   /* ----------------------------------------------------------------------- */
3915   /* draw things in front of player (active dynamite or dynabombs)           */
3916   /* ----------------------------------------------------------------------- */
3917
3918   if (IS_ACTIVE_BOMB(element))
3919   {
3920     graphic = el2img(element);
3921     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3922
3923     if (game.emulation == EMU_SUPAPLEX)
3924       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3925     else
3926       DrawGraphicThruMask(sx, sy, graphic, frame);
3927   }
3928
3929   if (player_is_moving && last_element == EL_EXPLOSION)
3930   {
3931     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3932                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
3933     int graphic = el_act2img(element, ACTION_EXPLODING);
3934     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3935     int phase = ExplodePhase[last_jx][last_jy] - 1;
3936     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3937
3938     if (phase >= delay)
3939       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3940   }
3941
3942   /* ----------------------------------------------------------------------- */
3943   /* draw elements the player is just walking/passing through/under          */
3944   /* ----------------------------------------------------------------------- */
3945
3946   if (player_is_moving)
3947   {
3948     /* handle the field the player is leaving ... */
3949     if (IS_ACCESSIBLE_INSIDE(last_element))
3950       DrawLevelField(last_jx, last_jy);
3951     else if (IS_ACCESSIBLE_UNDER(last_element))
3952       DrawLevelFieldThruMask(last_jx, last_jy);
3953   }
3954
3955   /* do not redraw accessible elements if the player is just pushing them */
3956   if (!player_is_moving || !player->is_pushing)
3957   {
3958     /* ... and the field the player is entering */
3959     if (IS_ACCESSIBLE_INSIDE(element))
3960       DrawLevelField(jx, jy);
3961     else if (IS_ACCESSIBLE_UNDER(element))
3962       DrawLevelFieldThruMask(jx, jy);
3963  &n