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