fixed graphical bugs caused by commit f77bbb8f and 1b4dc759
[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       fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1069     BackToFront();
1070
1071   SetScreenStates_BeforeFadingOut();
1072
1073   SetTileCursorActive(FALSE);
1074   SetOverlayActive(FALSE);
1075
1076 #if 0
1077   DrawMaskedBorder(REDRAW_ALL);
1078 #endif
1079
1080   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1081     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1082   else
1083     FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1084
1085   SetScreenStates_AfterFadingOut();
1086 }
1087
1088 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1089 {
1090   static struct TitleFadingInfo fading_leave_stored;
1091
1092   if (set)
1093     fading_leave_stored = fading_leave;
1094   else
1095     fading = fading_leave_stored;
1096 }
1097
1098 void FadeSetEnterMenu(void)
1099 {
1100   fading = menu.enter_menu;
1101
1102   FadeSetLeaveNext(fading, TRUE);       // (keep same fade mode)
1103 }
1104
1105 void FadeSetLeaveMenu(void)
1106 {
1107   fading = menu.leave_menu;
1108
1109   FadeSetLeaveNext(fading, TRUE);       // (keep same fade mode)
1110 }
1111
1112 void FadeSetEnterScreen(void)
1113 {
1114   fading = menu.enter_screen[game_status];
1115
1116   FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);       // store
1117 }
1118
1119 void FadeSetNextScreen(void)
1120 {
1121   fading = menu.next_screen[game_status];
1122
1123   // (do not overwrite fade mode set by FadeSetEnterScreen)
1124   // FadeSetLeaveNext(fading, TRUE);    // (keep same fade mode)
1125 }
1126
1127 void FadeSetLeaveScreen(void)
1128 {
1129   FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);      // recall
1130 }
1131
1132 void FadeSetFromType(int type)
1133 {
1134   if (type & TYPE_ENTER_SCREEN)
1135     FadeSetEnterScreen();
1136   else if (type & TYPE_ENTER)
1137     FadeSetEnterMenu();
1138   else if (type & TYPE_LEAVE)
1139     FadeSetLeaveMenu();
1140 }
1141
1142 void FadeSetDisabled(void)
1143 {
1144   static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1145
1146   fading = fading_none;
1147 }
1148
1149 void FadeSkipNextFadeIn(void)
1150 {
1151   FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1152 }
1153
1154 void FadeSkipNextFadeOut(void)
1155 {
1156   FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1157 }
1158
1159 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1160 {
1161   if (graphic == IMG_UNDEFINED)
1162     return NULL;
1163
1164   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1165
1166   return (graphic_info[graphic].bitmap != NULL || redefined ?
1167           graphic_info[graphic].bitmap :
1168           graphic_info[default_graphic].bitmap);
1169 }
1170
1171 static Bitmap *getBackgroundBitmap(int graphic)
1172 {
1173   return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1174 }
1175
1176 static Bitmap *getGlobalBorderBitmap(int graphic)
1177 {
1178   return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1179 }
1180
1181 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1182 {
1183   int graphic =
1184     (status == GAME_MODE_MAIN ||
1185      status == GAME_MODE_PSEUDO_TYPENAME        ? IMG_GLOBAL_BORDER_MAIN :
1186      status == GAME_MODE_SCORES                 ? IMG_GLOBAL_BORDER_SCORES :
1187      status == GAME_MODE_EDITOR                 ? IMG_GLOBAL_BORDER_EDITOR :
1188      status == GAME_MODE_PLAYING                ? IMG_GLOBAL_BORDER_PLAYING :
1189      IMG_GLOBAL_BORDER);
1190
1191   return getGlobalBorderBitmap(graphic);
1192 }
1193
1194 void SetWindowBackgroundImageIfDefined(int graphic)
1195 {
1196   if (graphic_info[graphic].bitmap)
1197     SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1198 }
1199
1200 void SetMainBackgroundImageIfDefined(int graphic)
1201 {
1202   if (graphic_info[graphic].bitmap)
1203     SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1204 }
1205
1206 void SetDoorBackgroundImageIfDefined(int graphic)
1207 {
1208   if (graphic_info[graphic].bitmap)
1209     SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1210 }
1211
1212 void SetWindowBackgroundImage(int graphic)
1213 {
1214   SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1215 }
1216
1217 void SetMainBackgroundImage(int graphic)
1218 {
1219   SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1220 }
1221
1222 void SetDoorBackgroundImage(int graphic)
1223 {
1224   SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1225 }
1226
1227 void SetPanelBackground(void)
1228 {
1229   struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1230
1231   BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1232                   gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1233
1234   SetDoorBackgroundBitmap(bitmap_db_panel);
1235 }
1236
1237 void DrawBackground(int x, int y, int width, int height)
1238 {
1239   // "drawto" might still point to playfield buffer here (hall of fame)
1240   ClearRectangleOnBackground(backbuffer, x, y, width, height);
1241
1242   if (IN_GFX_FIELD_FULL(x, y))
1243     redraw_mask |= REDRAW_FIELD;
1244   else if (IN_GFX_DOOR_1(x, y))
1245     redraw_mask |= REDRAW_DOOR_1;
1246   else if (IN_GFX_DOOR_2(x, y))
1247     redraw_mask |= REDRAW_DOOR_2;
1248   else if (IN_GFX_DOOR_3(x, y))
1249     redraw_mask |= REDRAW_DOOR_3;
1250 }
1251
1252 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1253 {
1254   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1255
1256   if (font->bitmap == NULL)
1257     return;
1258
1259   DrawBackground(x, y, width, height);
1260 }
1261
1262 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1263 {
1264   struct GraphicInfo *g = &graphic_info[graphic];
1265
1266   if (g->bitmap == NULL)
1267     return;
1268
1269   DrawBackground(x, y, width, height);
1270 }
1271
1272 static int game_status_last = -1;
1273 static Bitmap *global_border_bitmap_last = NULL;
1274 static Bitmap *global_border_bitmap = NULL;
1275 static int real_sx_last = -1, real_sy_last = -1;
1276 static int full_sxsize_last = -1, full_sysize_last = -1;
1277 static int dx_last = -1, dy_last = -1;
1278 static int dxsize_last = -1, dysize_last = -1;
1279 static int vx_last = -1, vy_last = -1;
1280 static int vxsize_last = -1, vysize_last = -1;
1281 static int ex_last = -1, ey_last = -1;
1282 static int exsize_last = -1, eysize_last = -1;
1283
1284 boolean CheckIfGlobalBorderHasChanged(void)
1285 {
1286   // if game status has not changed, global border has not changed either
1287   if (game_status == game_status_last)
1288     return FALSE;
1289
1290   // determine and store new global border bitmap for current game status
1291   global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1292
1293   return (global_border_bitmap_last != global_border_bitmap);
1294 }
1295
1296 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED             0
1297
1298 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1299 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1300 {
1301   // if game status has not changed, nothing has to be redrawn
1302   if (game_status == game_status_last)
1303     return FALSE;
1304
1305   // redraw if last screen was title screen
1306   if (game_status_last == GAME_MODE_TITLE)
1307     return TRUE;
1308
1309   // redraw if global screen border has changed
1310   if (CheckIfGlobalBorderHasChanged())
1311     return TRUE;
1312
1313   // redraw if position or size of playfield area has changed
1314   if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1315       full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1316     return TRUE;
1317
1318   // redraw if position or size of door area has changed
1319   if (dx_last != DX || dy_last != DY ||
1320       dxsize_last != DXSIZE || dysize_last != DYSIZE)
1321     return TRUE;
1322
1323   // redraw if position or size of tape area has changed
1324   if (vx_last != VX || vy_last != VY ||
1325       vxsize_last != VXSIZE || vysize_last != VYSIZE)
1326     return TRUE;
1327
1328   // redraw if position or size of editor area has changed
1329   if (ex_last != EX || ey_last != EY ||
1330       exsize_last != EXSIZE || eysize_last != EYSIZE)
1331     return TRUE;
1332
1333   return FALSE;
1334 }
1335 #endif
1336
1337 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1338 {
1339   if (bitmap)
1340     BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1341   else
1342     ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1343 }
1344
1345 void RedrawGlobalBorder(void)
1346 {
1347   Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1348
1349   RedrawGlobalBorderFromBitmap(bitmap);
1350
1351   redraw_mask = REDRAW_ALL;
1352 }
1353
1354 static void RedrawGlobalBorderIfNeeded(void)
1355 {
1356 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1357   if (game_status == game_status_last)
1358     return;
1359 #endif
1360
1361   // copy current draw buffer to later copy back areas that have not changed
1362   if (game_status_last != GAME_MODE_TITLE)
1363     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1364
1365 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1366   if (CheckIfGlobalBorderRedrawIsNeeded())
1367 #endif
1368   {
1369     // redraw global screen border (or clear, if defined to be empty)
1370     RedrawGlobalBorderFromBitmap(global_border_bitmap);
1371
1372     if (game_status == GAME_MODE_EDITOR)
1373       DrawSpecialEditorDoor();
1374
1375     // copy previous playfield and door areas, if they are defined on both
1376     // previous and current screen and if they still have the same size
1377
1378     if (real_sx_last != -1 && real_sy_last != -1 &&
1379         REAL_SX != -1 && REAL_SY != -1 &&
1380         full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1381       BlitBitmap(bitmap_db_store_1, backbuffer,
1382                  real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1383                  REAL_SX, REAL_SY);
1384
1385     if (dx_last != -1 && dy_last != -1 &&
1386         DX != -1 && DY != -1 &&
1387         dxsize_last == DXSIZE && dysize_last == DYSIZE)
1388       BlitBitmap(bitmap_db_store_1, backbuffer,
1389                  dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1390
1391     if (game_status != GAME_MODE_EDITOR)
1392     {
1393       if (vx_last != -1 && vy_last != -1 &&
1394           VX != -1 && VY != -1 &&
1395           vxsize_last == VXSIZE && vysize_last == VYSIZE)
1396         BlitBitmap(bitmap_db_store_1, backbuffer,
1397                    vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1398     }
1399     else
1400     {
1401       if (ex_last != -1 && ey_last != -1 &&
1402           EX != -1 && EY != -1 &&
1403           exsize_last == EXSIZE && eysize_last == EYSIZE)
1404         BlitBitmap(bitmap_db_store_1, backbuffer,
1405                    ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1406     }
1407
1408     redraw_mask = REDRAW_ALL;
1409   }
1410
1411   game_status_last = game_status;
1412
1413   global_border_bitmap_last = global_border_bitmap;
1414
1415   real_sx_last = REAL_SX;
1416   real_sy_last = REAL_SY;
1417   full_sxsize_last = FULL_SXSIZE;
1418   full_sysize_last = FULL_SYSIZE;
1419   dx_last = DX;
1420   dy_last = DY;
1421   dxsize_last = DXSIZE;
1422   dysize_last = DYSIZE;
1423   vx_last = VX;
1424   vy_last = VY;
1425   vxsize_last = VXSIZE;
1426   vysize_last = VYSIZE;
1427   ex_last = EX;
1428   ey_last = EY;
1429   exsize_last = EXSIZE;
1430   eysize_last = EYSIZE;
1431 }
1432
1433 void ClearField(void)
1434 {
1435   RedrawGlobalBorderIfNeeded();
1436
1437   // !!! "drawto" might still point to playfield buffer here (see above) !!!
1438   // (when entering hall of fame after playing)
1439   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1440
1441   // !!! maybe this should be done before clearing the background !!!
1442   if (game_status == GAME_MODE_PLAYING)
1443   {
1444     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1445     SetDrawtoField(DRAW_TO_FIELDBUFFER);
1446   }
1447   else
1448   {
1449     SetDrawtoField(DRAW_TO_BACKBUFFER);
1450   }
1451 }
1452
1453 void MarkTileDirty(int x, int y)
1454 {
1455   redraw_mask |= REDRAW_FIELD;
1456 }
1457
1458 void SetBorderElement(void)
1459 {
1460   int x, y;
1461
1462   BorderElement = EL_EMPTY;
1463
1464   // the MM game engine does not use a visible border element
1465   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1466     return;
1467
1468   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1469   {
1470     for (x = 0; x < lev_fieldx; x++)
1471     {
1472       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1473         BorderElement = EL_STEELWALL;
1474
1475       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1476         x = lev_fieldx - 2;
1477     }
1478   }
1479 }
1480
1481 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1482                        int max_array_fieldx, int max_array_fieldy,
1483                        short field[max_array_fieldx][max_array_fieldy],
1484                        int max_fieldx, int max_fieldy)
1485 {
1486   int i,x,y;
1487   int old_element;
1488   static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1489   static int safety = 0;
1490
1491   // check if starting field still has the desired content
1492   if (field[from_x][from_y] == fill_element)
1493     return;
1494
1495   safety++;
1496
1497   if (safety > max_fieldx * max_fieldy)
1498     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1499
1500   old_element = field[from_x][from_y];
1501   field[from_x][from_y] = fill_element;
1502
1503   for (i = 0; i < 4; i++)
1504   {
1505     x = from_x + check[i][0];
1506     y = from_y + check[i][1];
1507
1508     if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1509       FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1510                         field, max_fieldx, max_fieldy);
1511   }
1512
1513   safety--;
1514 }
1515
1516 void FloodFillLevel(int from_x, int from_y, int fill_element,
1517                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1518                     int max_fieldx, int max_fieldy)
1519 {
1520   FloodFillLevelExt(from_x, from_y, fill_element,
1521                     MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1522                     max_fieldx, max_fieldy);
1523 }
1524
1525 void SetRandomAnimationValue(int x, int y)
1526 {
1527   gfx.anim_random_frame = GfxRandom[x][y];
1528 }
1529
1530 int getGraphicAnimationFrame(int graphic, int sync_frame)
1531 {
1532   // animation synchronized with global frame counter, not move position
1533   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1534     sync_frame = FrameCounter;
1535
1536   return getAnimationFrame(graphic_info[graphic].anim_frames,
1537                            graphic_info[graphic].anim_delay,
1538                            graphic_info[graphic].anim_mode,
1539                            graphic_info[graphic].anim_start_frame,
1540                            sync_frame);
1541 }
1542
1543 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1544 {
1545   struct GraphicInfo *g = &graphic_info[graphic];
1546   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1547
1548   if (tilesize == gfx.standard_tile_size)
1549     *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1550   else if (tilesize == game.tile_size)
1551     *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1552   else
1553     *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1554 }
1555
1556 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1557                         boolean get_backside)
1558 {
1559   struct GraphicInfo *g = &graphic_info[graphic];
1560   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1561   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1562
1563   if (g->offset_y == 0)         // frames are ordered horizontally
1564   {
1565     int max_width = g->anim_frames_per_line * g->width;
1566     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1567
1568     *x = pos % max_width;
1569     *y = src_y % g->height + pos / max_width * g->height;
1570   }
1571   else if (g->offset_x == 0)    // frames are ordered vertically
1572   {
1573     int max_height = g->anim_frames_per_line * g->height;
1574     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1575
1576     *x = src_x % g->width + pos / max_height * g->width;
1577     *y = pos % max_height;
1578   }
1579   else                          // frames are ordered diagonally
1580   {
1581     *x = src_x + frame * g->offset_x;
1582     *y = src_y + frame * g->offset_y;
1583   }
1584 }
1585
1586 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1587                               Bitmap **bitmap, int *x, int *y,
1588                               boolean get_backside)
1589 {
1590   struct GraphicInfo *g = &graphic_info[graphic];
1591
1592   // if no graphics defined at all, use fallback graphics
1593   if (g->bitmaps == NULL)
1594     *g = graphic_info[IMG_CHAR_EXCLAM];
1595
1596   // if no in-game graphics defined, always use standard graphic size
1597   if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1598     tilesize = TILESIZE;
1599
1600   getGraphicSourceBitmap(graphic, tilesize, bitmap);
1601   getGraphicSourceXY(graphic, frame, x, y, get_backside);
1602
1603   *x = *x * tilesize / g->tile_size;
1604   *y = *y * tilesize / g->tile_size;
1605 }
1606
1607 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1608                            Bitmap **bitmap, int *x, int *y)
1609 {
1610   getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1611 }
1612
1613 void getFixedGraphicSource(int graphic, int frame,
1614                            Bitmap **bitmap, int *x, int *y)
1615 {
1616   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1617 }
1618
1619 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1620 {
1621   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1622 }
1623
1624 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1625                                 int *x, int *y, boolean get_backside)
1626 {
1627   getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1628                            get_backside);
1629 }
1630
1631 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1632 {
1633   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1634 }
1635
1636 void DrawGraphic(int x, int y, int graphic, int frame)
1637 {
1638 #if DEBUG
1639   if (!IN_SCR_FIELD(x, y))
1640   {
1641     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1642     printf("DrawGraphic(): This should never happen!\n");
1643     return;
1644   }
1645 #endif
1646
1647   DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1648                  frame);
1649
1650   MarkTileDirty(x, y);
1651 }
1652
1653 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1654 {
1655 #if DEBUG
1656   if (!IN_SCR_FIELD(x, y))
1657   {
1658     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1659     printf("DrawGraphic(): This should never happen!\n");
1660     return;
1661   }
1662 #endif
1663
1664   DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1665                       frame);
1666   MarkTileDirty(x, y);
1667 }
1668
1669 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1670                     int frame)
1671 {
1672   Bitmap *src_bitmap;
1673   int src_x, src_y;
1674
1675   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1676
1677   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1678 }
1679
1680 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1681                          int frame)
1682 {
1683   Bitmap *src_bitmap;
1684   int src_x, src_y;
1685
1686   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1687   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1688 }
1689
1690 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1691 {
1692 #if DEBUG
1693   if (!IN_SCR_FIELD(x, y))
1694   {
1695     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1696     printf("DrawGraphicThruMask(): This should never happen!\n");
1697     return;
1698   }
1699 #endif
1700
1701   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1702                          graphic, frame);
1703
1704   MarkTileDirty(x, y);
1705 }
1706
1707 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1708 {
1709 #if DEBUG
1710   if (!IN_SCR_FIELD(x, y))
1711   {
1712     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1713     printf("DrawGraphicThruMask(): This should never happen!\n");
1714     return;
1715   }
1716 #endif
1717
1718   DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1719                               graphic, frame);
1720   MarkTileDirty(x, y);
1721 }
1722
1723 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1724                             int frame)
1725 {
1726   Bitmap *src_bitmap;
1727   int src_x, src_y;
1728
1729   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1730
1731   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1732                    dst_x, dst_y);
1733 }
1734
1735 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1736                                  int graphic, int frame)
1737 {
1738   Bitmap *src_bitmap;
1739   int src_x, src_y;
1740
1741   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1742
1743   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1744                    dst_x, dst_y);
1745 }
1746
1747 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1748 {
1749   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1750                       frame, tilesize);
1751   MarkTileDirty(x / tilesize, y / tilesize);
1752 }
1753
1754 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1755                               int tilesize)
1756 {
1757   DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1758                               graphic, frame, tilesize);
1759   MarkTileDirty(x / tilesize, y / tilesize);
1760 }
1761
1762 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1763                          int tilesize)
1764 {
1765   Bitmap *src_bitmap;
1766   int src_x, src_y;
1767
1768   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1769   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1770 }
1771
1772 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1773                                  int frame, int tilesize)
1774 {
1775   Bitmap *src_bitmap;
1776   int src_x, src_y;
1777
1778   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1779   BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1780 }
1781
1782 void DrawMiniGraphic(int x, int y, int graphic)
1783 {
1784   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1785   MarkTileDirty(x / 2, y / 2);
1786 }
1787
1788 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1789 {
1790   Bitmap *src_bitmap;
1791   int src_x, src_y;
1792
1793   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1794   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1795 }
1796
1797 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1798                                      int graphic, int frame,
1799                                      int cut_mode, int mask_mode)
1800 {
1801   Bitmap *src_bitmap;
1802   int src_x, src_y;
1803   int dst_x, dst_y;
1804   int width = TILEX, height = TILEY;
1805   int cx = 0, cy = 0;
1806
1807   if (dx || dy)                 // shifted graphic
1808   {
1809     if (x < BX1)                // object enters playfield from the left
1810     {
1811       x = BX1;
1812       width = dx;
1813       cx = TILEX - dx;
1814       dx = 0;
1815     }
1816     else if (x > BX2)           // object enters playfield from the right
1817     {
1818       x = BX2;
1819       width = -dx;
1820       dx = TILEX + dx;
1821     }
1822     else if (x == BX1 && dx < 0) // object leaves playfield to the left
1823     {
1824       width += dx;
1825       cx = -dx;
1826       dx = 0;
1827     }
1828     else if (x == BX2 && dx > 0) // object leaves playfield to the right
1829       width -= dx;
1830     else if (dx)                // general horizontal movement
1831       MarkTileDirty(x + SIGN(dx), y);
1832
1833     if (y < BY1)                // object enters playfield from the top
1834     {
1835       if (cut_mode == CUT_BELOW) // object completely above top border
1836         return;
1837
1838       y = BY1;
1839       height = dy;
1840       cy = TILEY - dy;
1841       dy = 0;
1842     }
1843     else if (y > BY2)           // object enters playfield from the bottom
1844     {
1845       y = BY2;
1846       height = -dy;
1847       dy = TILEY + dy;
1848     }
1849     else if (y == BY1 && dy < 0) // object leaves playfield to the top
1850     {
1851       height += dy;
1852       cy = -dy;
1853       dy = 0;
1854     }
1855     else if (dy > 0 && cut_mode == CUT_ABOVE)
1856     {
1857       if (y == BY2)             // object completely above bottom border
1858         return;
1859
1860       height = dy;
1861       cy = TILEY - dy;
1862       dy = TILEY;
1863       MarkTileDirty(x, y + 1);
1864     }                           // object leaves playfield to the bottom
1865     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1866       height -= dy;
1867     else if (dy)                // general vertical movement
1868       MarkTileDirty(x, y + SIGN(dy));
1869   }
1870
1871 #if DEBUG
1872   if (!IN_SCR_FIELD(x, y))
1873   {
1874     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1875     printf("DrawGraphicShifted(): This should never happen!\n");
1876     return;
1877   }
1878 #endif
1879
1880   width = width * TILESIZE_VAR / TILESIZE;
1881   height = height * TILESIZE_VAR / TILESIZE;
1882   cx = cx * TILESIZE_VAR / TILESIZE;
1883   cy = cy * TILESIZE_VAR / TILESIZE;
1884   dx = dx * TILESIZE_VAR / TILESIZE;
1885   dy = dy * TILESIZE_VAR / TILESIZE;
1886
1887   if (width > 0 && height > 0)
1888   {
1889     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1890
1891     src_x += cx;
1892     src_y += cy;
1893
1894     dst_x = FX + x * TILEX_VAR + dx;
1895     dst_y = FY + y * TILEY_VAR + dy;
1896
1897     if (mask_mode == USE_MASKING)
1898       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1899                        dst_x, dst_y);
1900     else
1901       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1902                  dst_x, dst_y);
1903
1904     MarkTileDirty(x, y);
1905   }
1906 }
1907
1908 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1909                                      int graphic, int frame,
1910                                      int cut_mode, int mask_mode)
1911 {
1912   Bitmap *src_bitmap;
1913   int src_x, src_y;
1914   int dst_x, dst_y;
1915   int width = TILEX_VAR, height = TILEY_VAR;
1916   int x1 = x;
1917   int y1 = y;
1918   int x2 = x + SIGN(dx);
1919   int y2 = y + SIGN(dy);
1920
1921   // movement with two-tile animations must be sync'ed with movement position,
1922   // not with current GfxFrame (which can be higher when using slow movement)
1923   int anim_pos = (dx ? ABS(dx) : ABS(dy));
1924   int anim_frames = graphic_info[graphic].anim_frames;
1925
1926   // (we also need anim_delay here for movement animations with less frames)
1927   int anim_delay = graphic_info[graphic].anim_delay;
1928   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1929
1930   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    // only for falling!
1931   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    // only for falling!
1932
1933   // re-calculate animation frame for two-tile movement animation
1934   frame = getGraphicAnimationFrame(graphic, sync_frame);
1935
1936   // check if movement start graphic inside screen area and should be drawn
1937   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1938   {
1939     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1940
1941     dst_x = FX + x1 * TILEX_VAR;
1942     dst_y = FY + y1 * TILEY_VAR;
1943
1944     if (mask_mode == USE_MASKING)
1945       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1946                        dst_x, dst_y);
1947     else
1948       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1949                  dst_x, dst_y);
1950
1951     MarkTileDirty(x1, y1);
1952   }
1953
1954   // check if movement end graphic inside screen area and should be drawn
1955   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1956   {
1957     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1958
1959     dst_x = FX + x2 * TILEX_VAR;
1960     dst_y = FY + y2 * TILEY_VAR;
1961
1962     if (mask_mode == USE_MASKING)
1963       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1964                        dst_x, dst_y);
1965     else
1966       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1967                  dst_x, dst_y);
1968
1969     MarkTileDirty(x2, y2);
1970   }
1971 }
1972
1973 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1974                                int graphic, int frame,
1975                                int cut_mode, int mask_mode)
1976 {
1977   if (graphic < 0)
1978   {
1979     DrawGraphic(x, y, graphic, frame);
1980
1981     return;
1982   }
1983
1984   if (graphic_info[graphic].double_movement)    // EM style movement images
1985     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1986   else
1987     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1988 }
1989
1990 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1991                                        int graphic, int frame, int cut_mode)
1992 {
1993   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1994 }
1995
1996 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1997                           int cut_mode, int mask_mode)
1998 {
1999   int lx = LEVELX(x), ly = LEVELY(y);
2000   int graphic;
2001   int frame;
2002
2003   if (IN_LEV_FIELD(lx, ly))
2004   {
2005     SetRandomAnimationValue(lx, ly);
2006
2007     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2008     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2009
2010     // do not use double (EM style) movement graphic when not moving
2011     if (graphic_info[graphic].double_movement && !dx && !dy)
2012     {
2013       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2014       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2015     }
2016   }
2017   else  // border element
2018   {
2019     graphic = el2img(element);
2020     frame = getGraphicAnimationFrame(graphic, -1);
2021   }
2022
2023   if (element == EL_EXPANDABLE_WALL)
2024   {
2025     boolean left_stopped = FALSE, right_stopped = FALSE;
2026
2027     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2028       left_stopped = TRUE;
2029     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2030       right_stopped = TRUE;
2031
2032     if (left_stopped && right_stopped)
2033       graphic = IMG_WALL;
2034     else if (left_stopped)
2035     {
2036       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2037       frame = graphic_info[graphic].anim_frames - 1;
2038     }
2039     else if (right_stopped)
2040     {
2041       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2042       frame = graphic_info[graphic].anim_frames - 1;
2043     }
2044   }
2045
2046   if (dx || dy)
2047     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2048   else if (mask_mode == USE_MASKING)
2049     DrawGraphicThruMask(x, y, graphic, frame);
2050   else
2051     DrawGraphic(x, y, graphic, frame);
2052 }
2053
2054 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2055                          int cut_mode, int mask_mode)
2056 {
2057   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2058     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2059                          cut_mode, mask_mode);
2060 }
2061
2062 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2063                               int cut_mode)
2064 {
2065   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2066 }
2067
2068 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2069                              int cut_mode)
2070 {
2071   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2072 }
2073
2074 void DrawLevelElementThruMask(int x, int y, int element)
2075 {
2076   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2077 }
2078
2079 void DrawLevelFieldThruMask(int x, int y)
2080 {
2081   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2082 }
2083
2084 // !!! implementation of quicksand is totally broken !!!
2085 #define IS_CRUMBLED_TILE(x, y, e)                                       \
2086         (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
2087                              !IS_MOVING(x, y) ||                        \
2088                              (e) == EL_QUICKSAND_EMPTYING ||            \
2089                              (e) == EL_QUICKSAND_FAST_EMPTYING))
2090
2091 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2092                                                int graphic)
2093 {
2094   Bitmap *src_bitmap;
2095   int src_x, src_y;
2096   int width, height, cx, cy;
2097   int sx = SCREENX(x), sy = SCREENY(y);
2098   int crumbled_border_size = graphic_info[graphic].border_size;
2099   int crumbled_tile_size = graphic_info[graphic].tile_size;
2100   int crumbled_border_size_var =
2101     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2102   int i;
2103
2104   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2105
2106   for (i = 1; i < 4; i++)
2107   {
2108     int dxx = (i & 1 ? dx : 0);
2109     int dyy = (i & 2 ? dy : 0);
2110     int xx = x + dxx;
2111     int yy = y + dyy;
2112     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2113                    BorderElement);
2114
2115     // check if neighbour field is of same crumble type
2116     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2117                     graphic_info[graphic].class ==
2118                     graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2119
2120     // return if check prevents inner corner
2121     if (same == (dxx == dx && dyy == dy))
2122       return;
2123   }
2124
2125   // if we reach this point, we have an inner corner
2126
2127   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2128
2129   width  = crumbled_border_size_var;
2130   height = crumbled_border_size_var;
2131   cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
2132   cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2133
2134   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2135              width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2136 }
2137
2138 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2139                                           int dir)
2140 {
2141   Bitmap *src_bitmap;
2142   int src_x, src_y;
2143   int width, height, bx, by, cx, cy;
2144   int sx = SCREENX(x), sy = SCREENY(y);
2145   int crumbled_border_size = graphic_info[graphic].border_size;
2146   int crumbled_tile_size = graphic_info[graphic].tile_size;
2147   int crumbled_border_size_var =
2148     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2149   int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2150   int i;
2151
2152   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2153
2154   // draw simple, sloppy, non-corner-accurate crumbled border
2155
2156   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2157   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2158   cx = (dir == 2 ? crumbled_border_pos_var : 0);
2159   cy = (dir == 3 ? crumbled_border_pos_var : 0);
2160
2161   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2162              FX + sx * TILEX_VAR + cx,
2163              FY + sy * TILEY_VAR + cy);
2164
2165   // (remaining middle border part must be at least as big as corner part)
2166   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2167       crumbled_border_size_var >= TILESIZE_VAR / 3)
2168     return;
2169
2170   // correct corners of crumbled border, if needed
2171
2172   for (i = -1; i <= 1; i += 2)
2173   {
2174     int xx = x + (dir == 0 || dir == 3 ? i : 0);
2175     int yy = y + (dir == 1 || dir == 2 ? i : 0);
2176     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2177                    BorderElement);
2178
2179     // check if neighbour field is of same crumble type
2180     if (IS_CRUMBLED_TILE(xx, yy, element) &&
2181         graphic_info[graphic].class ==
2182         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2183     {
2184       // no crumbled corner, but continued crumbled border
2185
2186       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2187       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2188       int b1 = (i == 1 ? crumbled_border_size_var :
2189                 TILESIZE_VAR - 2 * crumbled_border_size_var);
2190
2191       width  = crumbled_border_size_var;
2192       height = crumbled_border_size_var;
2193
2194       if (dir == 1 || dir == 2)
2195       {
2196         cx = c1;
2197         cy = c2;
2198         bx = cx;
2199         by = b1;
2200       }
2201       else
2202       {
2203         cx = c2;
2204         cy = c1;
2205         bx = b1;
2206         by = cy;
2207       }
2208
2209       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2210                  width, height,
2211                  FX + sx * TILEX_VAR + cx,
2212                  FY + sy * TILEY_VAR + cy);
2213     }
2214   }
2215 }
2216
2217 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2218 {
2219   int sx = SCREENX(x), sy = SCREENY(y);
2220   int element;
2221   int i;
2222   static int xy[4][2] =
2223   {
2224     { 0, -1 },
2225     { -1, 0 },
2226     { +1, 0 },
2227     { 0, +1 }
2228   };
2229
2230   if (!IN_LEV_FIELD(x, y))
2231     return;
2232
2233   element = TILE_GFX_ELEMENT(x, y);
2234
2235   if (IS_CRUMBLED_TILE(x, y, element))          // crumble field itself
2236   {
2237     if (!IN_SCR_FIELD(sx, sy))
2238       return;
2239
2240     // crumble field borders towards direct neighbour fields
2241     for (i = 0; i < 4; i++)
2242     {
2243       int xx = x + xy[i][0];
2244       int yy = y + xy[i][1];
2245
2246       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2247                  BorderElement);
2248
2249       // check if neighbour field is of same crumble type
2250       if (IS_CRUMBLED_TILE(xx, yy, element) &&
2251           graphic_info[graphic].class ==
2252           graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2253         continue;
2254
2255       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2256     }
2257
2258     // crumble inner field corners towards corner neighbour fields
2259     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2260         graphic_info[graphic].anim_frames == 2)
2261     {
2262       for (i = 0; i < 4; i++)
2263       {
2264         int dx = (i & 1 ? +1 : -1);
2265         int dy = (i & 2 ? +1 : -1);
2266
2267         DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2268       }
2269     }
2270
2271     MarkTileDirty(sx, sy);
2272   }
2273   else          // center field is not crumbled -- crumble neighbour fields
2274   {
2275     // crumble field borders of direct neighbour fields
2276     for (i = 0; i < 4; i++)
2277     {
2278       int xx = x + xy[i][0];
2279       int yy = y + xy[i][1];
2280       int sxx = sx + xy[i][0];
2281       int syy = sy + xy[i][1];
2282
2283       if (!IN_LEV_FIELD(xx, yy) ||
2284           !IN_SCR_FIELD(sxx, syy))
2285         continue;
2286
2287       // do not crumble fields that are being digged or snapped
2288       if (Feld[xx][yy] == EL_EMPTY ||
2289           Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2290         continue;
2291
2292       element = TILE_GFX_ELEMENT(xx, yy);
2293
2294       if (!IS_CRUMBLED_TILE(xx, yy, element))
2295         continue;
2296
2297       graphic = el_act2crm(element, ACTION_DEFAULT);
2298
2299       DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2300
2301       MarkTileDirty(sxx, syy);
2302     }
2303
2304     // crumble inner field corners of corner neighbour fields
2305     for (i = 0; i < 4; i++)
2306     {
2307       int dx = (i & 1 ? +1 : -1);
2308       int dy = (i & 2 ? +1 : -1);
2309       int xx = x + dx;
2310       int yy = y + dy;
2311       int sxx = sx + dx;
2312       int syy = sy + dy;
2313
2314       if (!IN_LEV_FIELD(xx, yy) ||
2315           !IN_SCR_FIELD(sxx, syy))
2316         continue;
2317
2318       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2319         continue;
2320
2321       element = TILE_GFX_ELEMENT(xx, yy);
2322
2323       if (!IS_CRUMBLED_TILE(xx, yy, element))
2324         continue;
2325
2326       graphic = el_act2crm(element, ACTION_DEFAULT);
2327
2328       if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2329           graphic_info[graphic].anim_frames == 2)
2330         DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2331
2332       MarkTileDirty(sxx, syy);
2333     }
2334   }
2335 }
2336
2337 void DrawLevelFieldCrumbled(int x, int y)
2338 {
2339   int graphic;
2340
2341   if (!IN_LEV_FIELD(x, y))
2342     return;
2343
2344   if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2345       GfxElement[x][y] != EL_UNDEFINED &&
2346       GFX_CRUMBLED(GfxElement[x][y]))
2347   {
2348     DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2349
2350     return;
2351   }
2352
2353   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2354
2355   DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2356 }
2357
2358 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2359                                    int step_frame)
2360 {
2361   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2362   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2363   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2364   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2365   int sx = SCREENX(x), sy = SCREENY(y);
2366
2367   DrawGraphic(sx, sy, graphic1, frame1);
2368   DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2369 }
2370
2371 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2372 {
2373   int sx = SCREENX(x), sy = SCREENY(y);
2374   static int xy[4][2] =
2375   {
2376     { 0, -1 },
2377     { -1, 0 },
2378     { +1, 0 },
2379     { 0, +1 }
2380   };
2381   int i;
2382
2383   // crumble direct neighbour fields (required for field borders)
2384   for (i = 0; i < 4; i++)
2385   {
2386     int xx = x + xy[i][0];
2387     int yy = y + xy[i][1];
2388     int sxx = sx + xy[i][0];
2389     int syy = sy + xy[i][1];
2390
2391     if (!IN_LEV_FIELD(xx, yy) ||
2392         !IN_SCR_FIELD(sxx, syy) ||
2393         !GFX_CRUMBLED(Feld[xx][yy]) ||
2394         IS_MOVING(xx, yy))
2395       continue;
2396
2397     DrawLevelField(xx, yy);
2398   }
2399
2400   // crumble corner neighbour fields (required for inner field corners)
2401   for (i = 0; i < 4; i++)
2402   {
2403     int dx = (i & 1 ? +1 : -1);
2404     int dy = (i & 2 ? +1 : -1);
2405     int xx = x + dx;
2406     int yy = y + dy;
2407     int sxx = sx + dx;
2408     int syy = sy + dy;
2409
2410     if (!IN_LEV_FIELD(xx, yy) ||
2411         !IN_SCR_FIELD(sxx, syy) ||
2412         !GFX_CRUMBLED(Feld[xx][yy]) ||
2413         IS_MOVING(xx, yy))
2414       continue;
2415
2416     int element = TILE_GFX_ELEMENT(xx, yy);
2417     int graphic = el_act2crm(element, ACTION_DEFAULT);
2418
2419     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2420         graphic_info[graphic].anim_frames == 2)
2421       DrawLevelField(xx, yy);
2422   }
2423 }
2424
2425 static int getBorderElement(int x, int y)
2426 {
2427   int border[7][2] =
2428   {
2429     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
2430     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
2431     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
2432     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2433     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
2434     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
2435     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
2436   };
2437   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2438   int steel_position = (x == -1         && y == -1              ? 0 :
2439                         x == lev_fieldx && y == -1              ? 1 :
2440                         x == -1         && y == lev_fieldy      ? 2 :
2441                         x == lev_fieldx && y == lev_fieldy      ? 3 :
2442                         x == -1         || x == lev_fieldx      ? 4 :
2443                         y == -1         || y == lev_fieldy      ? 5 : 6);
2444
2445   return border[steel_position][steel_type];
2446 }
2447
2448 void DrawScreenElement(int x, int y, int element)
2449 {
2450   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2451   DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2452 }
2453
2454 void DrawLevelElement(int x, int y, int element)
2455 {
2456   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2457     DrawScreenElement(SCREENX(x), SCREENY(y), element);
2458 }
2459
2460 void DrawScreenField(int x, int y)
2461 {
2462   int lx = LEVELX(x), ly = LEVELY(y);
2463   int element, content;
2464
2465   if (!IN_LEV_FIELD(lx, ly))
2466   {
2467     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2468       element = EL_EMPTY;
2469     else
2470       element = getBorderElement(lx, ly);
2471
2472     DrawScreenElement(x, y, element);
2473
2474     return;
2475   }
2476
2477   element = Feld[lx][ly];
2478   content = Store[lx][ly];
2479
2480   if (IS_MOVING(lx, ly))
2481   {
2482     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2483     boolean cut_mode = NO_CUTTING;
2484
2485     if (element == EL_QUICKSAND_EMPTYING ||
2486         element == EL_QUICKSAND_FAST_EMPTYING ||
2487         element == EL_MAGIC_WALL_EMPTYING ||
2488         element == EL_BD_MAGIC_WALL_EMPTYING ||
2489         element == EL_DC_MAGIC_WALL_EMPTYING ||
2490         element == EL_AMOEBA_DROPPING)
2491       cut_mode = CUT_ABOVE;
2492     else if (element == EL_QUICKSAND_FILLING ||
2493              element == EL_QUICKSAND_FAST_FILLING ||
2494              element == EL_MAGIC_WALL_FILLING ||
2495              element == EL_BD_MAGIC_WALL_FILLING ||
2496              element == EL_DC_MAGIC_WALL_FILLING)
2497       cut_mode = CUT_BELOW;
2498
2499     if (cut_mode == CUT_ABOVE)
2500       DrawScreenElement(x, y, element);
2501     else
2502       DrawScreenElement(x, y, EL_EMPTY);
2503
2504     if (horiz_move)
2505       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2506     else if (cut_mode == NO_CUTTING)
2507       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2508     else
2509     {
2510       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2511
2512       if (cut_mode == CUT_BELOW &&
2513           IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2514         DrawLevelElement(lx, ly + 1, element);
2515     }
2516
2517     if (content == EL_ACID)
2518     {
2519       int dir = MovDir[lx][ly];
2520       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2521       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2522
2523       DrawLevelElementThruMask(newlx, newly, EL_ACID);
2524
2525       // prevent target field from being drawn again (but without masking)
2526       // (this would happen if target field is scanned after moving element)
2527       Stop[newlx][newly] = TRUE;
2528     }
2529   }
2530   else if (IS_BLOCKED(lx, ly))
2531   {
2532     int oldx, oldy;
2533     int sx, sy;
2534     int horiz_move;
2535     boolean cut_mode = NO_CUTTING;
2536     int element_old, content_old;
2537
2538     Blocked2Moving(lx, ly, &oldx, &oldy);
2539     sx = SCREENX(oldx);
2540     sy = SCREENY(oldy);
2541     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2542                   MovDir[oldx][oldy] == MV_RIGHT);
2543
2544     element_old = Feld[oldx][oldy];
2545     content_old = Store[oldx][oldy];
2546
2547     if (element_old == EL_QUICKSAND_EMPTYING ||
2548         element_old == EL_QUICKSAND_FAST_EMPTYING ||
2549         element_old == EL_MAGIC_WALL_EMPTYING ||
2550         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2551         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2552         element_old == EL_AMOEBA_DROPPING)
2553       cut_mode = CUT_ABOVE;
2554
2555     DrawScreenElement(x, y, EL_EMPTY);
2556
2557     if (horiz_move)
2558       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2559                                NO_CUTTING);
2560     else if (cut_mode == NO_CUTTING)
2561       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2562                                cut_mode);
2563     else
2564       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2565                                cut_mode);
2566   }
2567   else if (IS_DRAWABLE(element))
2568     DrawScreenElement(x, y, element);
2569   else
2570     DrawScreenElement(x, y, EL_EMPTY);
2571 }
2572
2573 void DrawLevelField(int x, int y)
2574 {
2575   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2576     DrawScreenField(SCREENX(x), SCREENY(y));
2577   else if (IS_MOVING(x, y))
2578   {
2579     int newx,newy;
2580
2581     Moving2Blocked(x, y, &newx, &newy);
2582     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2583       DrawScreenField(SCREENX(newx), SCREENY(newy));
2584   }
2585   else if (IS_BLOCKED(x, y))
2586   {
2587     int oldx, oldy;
2588
2589     Blocked2Moving(x, y, &oldx, &oldy);
2590     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2591       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2592   }
2593 }
2594
2595 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2596                                 int (*el2img_function)(int), boolean masked,
2597                                 int element_bits_draw)
2598 {
2599   int element_base = map_mm_wall_element(element);
2600   int element_bits = (IS_DF_WALL(element) ?
2601                       element - EL_DF_WALL_START :
2602                       IS_MM_WALL(element) ?
2603                       element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2604   int graphic = el2img_function(element_base);
2605   int tilesize_draw = tilesize / 2;
2606   Bitmap *src_bitmap;
2607   int src_x, src_y;
2608   int i;
2609
2610   getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2611
2612   for (i = 0; i < 4; i++)
2613   {
2614     int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2615     int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2616
2617     if (!(element_bits_draw & (1 << i)))
2618       continue;
2619
2620     if (element_bits & (1 << i))
2621     {
2622       if (masked)
2623         BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2624                          tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2625       else
2626         BlitBitmap(src_bitmap, drawto, src_x, src_y,
2627                    tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2628     }
2629     else
2630     {
2631       if (!masked)
2632         ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2633                        tilesize_draw, tilesize_draw);
2634     }
2635   }
2636 }
2637
2638 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2639                            boolean masked, int element_bits_draw)
2640 {
2641   DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2642                       element, tilesize, el2edimg, masked, element_bits_draw);
2643 }
2644
2645 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2646                              int (*el2img_function)(int))
2647 {
2648   DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2649                       0x000f);
2650 }
2651
2652 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2653                                 boolean masked)
2654 {
2655   if (IS_MM_WALL(element))
2656   {
2657     DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2658                         element, tilesize, el2edimg, masked, 0x000f);
2659   }
2660   else
2661   {
2662     int graphic = el2edimg(element);
2663
2664     if (masked)
2665       DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2666     else
2667       DrawSizedGraphic(x, y, graphic, 0, tilesize);
2668   }
2669 }
2670
2671 void DrawSizedElement(int x, int y, int element, int tilesize)
2672 {
2673   DrawSizedElementExt(x, y, element, tilesize, FALSE);
2674 }
2675
2676 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2677 {
2678   DrawSizedElementExt(x, y, element, tilesize, TRUE);
2679 }
2680
2681 void DrawMiniElement(int x, int y, int element)
2682 {
2683   int graphic;
2684
2685   graphic = el2edimg(element);
2686   DrawMiniGraphic(x, y, graphic);
2687 }
2688
2689 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2690                             int tilesize)
2691 {
2692   int x = sx + scroll_x, y = sy + scroll_y;
2693
2694   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2695     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2696   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2697     DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2698   else
2699     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2700 }
2701
2702 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2703 {
2704   int x = sx + scroll_x, y = sy + scroll_y;
2705
2706   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2707     DrawMiniElement(sx, sy, EL_EMPTY);
2708   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2709     DrawMiniElement(sx, sy, Feld[x][y]);
2710   else
2711     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2712 }
2713
2714 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2715                                         int x, int y, int xsize, int ysize,
2716                                         int tile_width, int tile_height)
2717 {
2718   Bitmap *src_bitmap;
2719   int src_x, src_y;
2720   int dst_x = startx + x * tile_width;
2721   int dst_y = starty + y * tile_height;
2722   int width  = graphic_info[graphic].width;
2723   int height = graphic_info[graphic].height;
2724   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2725   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2726   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2727   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2728   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2729   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2730   boolean draw_masked = graphic_info[graphic].draw_masked;
2731
2732   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2733
2734   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2735   {
2736     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2737     return;
2738   }
2739
2740   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2741             inner_sx + (x - 1) * tile_width  % inner_width);
2742   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2743             inner_sy + (y - 1) * tile_height % inner_height);
2744
2745   if (draw_masked)
2746     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2747                      dst_x, dst_y);
2748   else
2749     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2750                dst_x, dst_y);
2751 }
2752
2753 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2754                                    int x, int y, int xsize, int ysize,
2755                                    int font_nr)
2756 {
2757   int font_width  = getFontWidth(font_nr);
2758   int font_height = getFontHeight(font_nr);
2759
2760   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2761                               font_width, font_height);
2762 }
2763
2764 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2765 {
2766   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2767   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2768   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2769   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2770   boolean no_delay = (tape.warp_forward);
2771   unsigned int anim_delay = 0;
2772   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2773   int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2774   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2775   int font_width = getFontWidth(font_nr);
2776   int font_height = getFontHeight(font_nr);
2777   int max_xsize = level.envelope[envelope_nr].xsize;
2778   int max_ysize = level.envelope[envelope_nr].ysize;
2779   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2780   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2781   int xend = max_xsize;
2782   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2783   int xstep = (xstart < xend ? 1 : 0);
2784   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2785   int start = 0;
2786   int end = MAX(xend - xstart, yend - ystart);
2787   int i;
2788
2789   for (i = start; i <= end; i++)
2790   {
2791     int last_frame = end;       // last frame of this "for" loop
2792     int x = xstart + i * xstep;
2793     int y = ystart + i * ystep;
2794     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2795     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2796     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2797     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2798     int xx, yy;
2799
2800     SetDrawtoField(DRAW_TO_FIELDBUFFER);
2801
2802     BlitScreenToBitmap(backbuffer);
2803
2804     SetDrawtoField(DRAW_TO_BACKBUFFER);
2805
2806     for (yy = 0; yy < ysize; yy++)
2807       for (xx = 0; xx < xsize; xx++)
2808         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2809
2810     DrawTextBuffer(sx + font_width, sy + font_height,
2811                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2812                    xsize - 2, ysize - 2, 0, mask_mode,
2813                    level.envelope[envelope_nr].autowrap,
2814                    level.envelope[envelope_nr].centered, FALSE);
2815
2816     redraw_mask |= REDRAW_FIELD;
2817     BackToFront();
2818
2819     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2820   }
2821
2822   ClearAutoRepeatKeyEvents();
2823 }
2824
2825 void ShowEnvelope(int envelope_nr)
2826 {
2827   int element = EL_ENVELOPE_1 + envelope_nr;
2828   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2829   int sound_opening = element_info[element].sound[ACTION_OPENING];
2830   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2831   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2832   boolean no_delay = (tape.warp_forward);
2833   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2834   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2835   int anim_mode = graphic_info[graphic].anim_mode;
2836   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2837                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2838
2839   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
2840
2841   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2842
2843   if (anim_mode == ANIM_DEFAULT)
2844     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2845
2846   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2847
2848   if (tape.playing)
2849     Delay(wait_delay_value);
2850   else
2851     WaitForEventToContinue();
2852
2853   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2854
2855   if (anim_mode != ANIM_NONE)
2856     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2857
2858   if (anim_mode == ANIM_DEFAULT)
2859     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2860
2861   game.envelope_active = FALSE;
2862
2863   SetDrawtoField(DRAW_TO_FIELDBUFFER);
2864
2865   redraw_mask |= REDRAW_FIELD;
2866   BackToFront();
2867 }
2868
2869 static void setRequestBasePosition(int *x, int *y)
2870 {
2871   int sx_base, sy_base;
2872
2873   if (request.x != -1)
2874     sx_base = request.x;
2875   else if (request.align == ALIGN_LEFT)
2876     sx_base = SX;
2877   else if (request.align == ALIGN_RIGHT)
2878     sx_base = SX + SXSIZE;
2879   else
2880     sx_base = SX + SXSIZE / 2;
2881
2882   if (request.y != -1)
2883     sy_base = request.y;
2884   else if (request.valign == VALIGN_TOP)
2885     sy_base = SY;
2886   else if (request.valign == VALIGN_BOTTOM)
2887     sy_base = SY + SYSIZE;
2888   else
2889     sy_base = SY + SYSIZE / 2;
2890
2891   *x = sx_base;
2892   *y = sy_base;
2893 }
2894
2895 static void setRequestPositionExt(int *x, int *y, int width, int height,
2896                                   boolean add_border_size)
2897 {
2898   int border_size = request.border_size;
2899   int sx_base, sy_base;
2900   int sx, sy;
2901
2902   setRequestBasePosition(&sx_base, &sy_base);
2903
2904   if (request.align == ALIGN_LEFT)
2905     sx = sx_base;
2906   else if (request.align == ALIGN_RIGHT)
2907     sx = sx_base - width;
2908   else
2909     sx = sx_base - width  / 2;
2910
2911   if (request.valign == VALIGN_TOP)
2912     sy = sy_base;
2913   else if (request.valign == VALIGN_BOTTOM)
2914     sy = sy_base - height;
2915   else
2916     sy = sy_base - height / 2;
2917
2918   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2919   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2920
2921   if (add_border_size)
2922   {
2923     sx += border_size;
2924     sy += border_size;
2925   }
2926
2927   *x = sx;
2928   *y = sy;
2929 }
2930
2931 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2932 {
2933   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2934 }
2935
2936 static void DrawEnvelopeRequest(char *text)
2937 {
2938   char *text_final = text;
2939   char *text_door_style = NULL;
2940   int graphic = IMG_BACKGROUND_REQUEST;
2941   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2942   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2943   int font_nr = FONT_REQUEST;
2944   int font_width = getFontWidth(font_nr);
2945   int font_height = getFontHeight(font_nr);
2946   int border_size = request.border_size;
2947   int line_spacing = request.line_spacing;
2948   int line_height = font_height + line_spacing;
2949   int max_text_width  = request.width  - 2 * border_size;
2950   int max_text_height = request.height - 2 * border_size;
2951   int line_length = max_text_width  / font_width;
2952   int max_lines   = max_text_height / line_height;
2953   int text_width = line_length * font_width;
2954   int width = request.width;
2955   int height = request.height;
2956   int tile_size = MAX(request.step_offset, 1);
2957   int x_steps = width  / tile_size;
2958   int y_steps = height / tile_size;
2959   int sx_offset = border_size;
2960   int sy_offset = border_size;
2961   int sx, sy;
2962   int i, x, y;
2963
2964   if (request.centered)
2965     sx_offset = (request.width - text_width) / 2;
2966
2967   if (request.wrap_single_words && !request.autowrap)
2968   {
2969     char *src_text_ptr, *dst_text_ptr;
2970
2971     text_door_style = checked_malloc(2 * strlen(text) + 1);
2972
2973     src_text_ptr = text;
2974     dst_text_ptr = text_door_style;
2975
2976     while (*src_text_ptr)
2977     {
2978       if (*src_text_ptr == ' ' ||
2979           *src_text_ptr == '?' ||
2980           *src_text_ptr == '!')
2981         *dst_text_ptr++ = '\n';
2982
2983       if (*src_text_ptr != ' ')
2984         *dst_text_ptr++ = *src_text_ptr;
2985
2986       src_text_ptr++;
2987     }
2988
2989     *dst_text_ptr = '\0';
2990
2991     text_final = text_door_style;
2992   }
2993
2994   setRequestPosition(&sx, &sy, FALSE);
2995
2996   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2997
2998   for (y = 0; y < y_steps; y++)
2999     for (x = 0; x < x_steps; x++)
3000       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3001                                   x, y, x_steps, y_steps,
3002                                   tile_size, tile_size);
3003
3004   // force DOOR font inside door area
3005   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3006
3007   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3008                  line_length, -1, max_lines, line_spacing, mask_mode,
3009                  request.autowrap, request.centered, FALSE);
3010
3011   ResetFontStatus();
3012
3013   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3014     RedrawGadget(tool_gadget[i]);
3015
3016   // store readily prepared envelope request for later use when animating
3017   BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3018
3019   if (text_door_style)
3020     free(text_door_style);
3021 }
3022
3023 static void AnimateEnvelopeRequest(int anim_mode, int action)
3024 {
3025   int graphic = IMG_BACKGROUND_REQUEST;
3026   boolean draw_masked = graphic_info[graphic].draw_masked;
3027   int delay_value_normal = request.step_delay;
3028   int delay_value_fast = delay_value_normal / 2;
3029   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3030   boolean no_delay = (tape.warp_forward);
3031   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3032   int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3033   unsigned int anim_delay = 0;
3034
3035   int tile_size = MAX(request.step_offset, 1);
3036   int max_xsize = request.width  / tile_size;
3037   int max_ysize = request.height / tile_size;
3038   int max_xsize_inner = max_xsize - 2;
3039   int max_ysize_inner = max_ysize - 2;
3040
3041   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3042   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3043   int xend = max_xsize_inner;
3044   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3045   int xstep = (xstart < xend ? 1 : 0);
3046   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3047   int start = 0;
3048   int end = MAX(xend - xstart, yend - ystart);
3049   int i;
3050
3051   if (setup.quick_doors)
3052   {
3053     xstart = xend;
3054     ystart = yend;
3055     end = 0;
3056   }
3057
3058   for (i = start; i <= end; i++)
3059   {
3060     int last_frame = end;       // last frame of this "for" loop
3061     int x = xstart + i * xstep;
3062     int y = ystart + i * ystep;
3063     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3064     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3065     int xsize_size_left = (xsize - 1) * tile_size;
3066     int ysize_size_top  = (ysize - 1) * tile_size;
3067     int max_xsize_pos = (max_xsize - 1) * tile_size;
3068     int max_ysize_pos = (max_ysize - 1) * tile_size;
3069     int width  = xsize * tile_size;
3070     int height = ysize * tile_size;
3071     int src_x, src_y;
3072     int dst_x, dst_y;
3073     int xx, yy;
3074
3075     setRequestPosition(&src_x, &src_y, FALSE);
3076     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3077
3078     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3079
3080     for (yy = 0; yy < 2; yy++)
3081     {
3082       for (xx = 0; xx < 2; xx++)
3083       {
3084         int src_xx = src_x + xx * max_xsize_pos;
3085         int src_yy = src_y + yy * max_ysize_pos;
3086         int dst_xx = dst_x + xx * xsize_size_left;
3087         int dst_yy = dst_y + yy * ysize_size_top;
3088         int xx_size = (xx ? tile_size : xsize_size_left);
3089         int yy_size = (yy ? tile_size : ysize_size_top);
3090
3091         if (draw_masked)
3092           BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3093                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3094         else
3095           BlitBitmap(bitmap_db_store_2, backbuffer,
3096                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3097       }
3098     }
3099
3100     redraw_mask |= REDRAW_FIELD;
3101
3102     BackToFront();
3103
3104     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3105   }
3106
3107   ClearAutoRepeatKeyEvents();
3108 }
3109
3110 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3111 {
3112   int graphic = IMG_BACKGROUND_REQUEST;
3113   int sound_opening = SND_REQUEST_OPENING;
3114   int sound_closing = SND_REQUEST_CLOSING;
3115   int anim_mode_1 = request.anim_mode;                  // (higher priority)
3116   int anim_mode_2 = graphic_info[graphic].anim_mode;    // (lower priority)
3117   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3118   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3119                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3120
3121   if (game_status == GAME_MODE_PLAYING)
3122     BlitScreenToBitmap(backbuffer);
3123
3124   SetDrawtoField(DRAW_TO_BACKBUFFER);
3125
3126   // SetDrawBackgroundMask(REDRAW_NONE);
3127
3128   if (action == ACTION_OPENING)
3129   {
3130     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3131
3132     if (req_state & REQ_ASK)
3133     {
3134       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3135       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3136     }
3137     else if (req_state & REQ_CONFIRM)
3138     {
3139       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3140     }
3141     else if (req_state & REQ_PLAYER)
3142     {
3143       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3144       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3145       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3146       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3147     }
3148
3149     DrawEnvelopeRequest(text);
3150   }
3151
3152   game.envelope_active = TRUE;  // needed for RedrawPlayfield() events
3153
3154   if (action == ACTION_OPENING)
3155   {
3156     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3157
3158     if (anim_mode == ANIM_DEFAULT)
3159       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3160
3161     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3162   }
3163   else
3164   {
3165     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3166
3167     if (anim_mode != ANIM_NONE)
3168       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3169
3170     if (anim_mode == ANIM_DEFAULT)
3171       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3172   }
3173
3174   game.envelope_active = FALSE;
3175
3176   if (action == ACTION_CLOSING)
3177     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3178
3179   // SetDrawBackgroundMask(last_draw_background_mask);
3180
3181   redraw_mask |= REDRAW_FIELD;
3182
3183   BackToFront();
3184
3185   if (action == ACTION_CLOSING &&
3186       game_status == GAME_MODE_PLAYING &&
3187       level.game_engine_type == GAME_ENGINE_TYPE_RND)
3188     SetDrawtoField(DRAW_TO_FIELDBUFFER);
3189 }
3190
3191 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3192 {
3193   if (IS_MM_WALL(element))
3194   {
3195     DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3196   }
3197   else
3198   {
3199     Bitmap *src_bitmap;
3200     int src_x, src_y;
3201     int graphic = el2preimg(element);
3202
3203     getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3204     BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3205                dst_x, dst_y);
3206   }
3207 }
3208
3209 void DrawLevel(int draw_background_mask)
3210 {
3211   int x,y;
3212
3213   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3214   SetDrawBackgroundMask(draw_background_mask);
3215
3216   ClearField();
3217
3218   for (x = BX1; x <= BX2; x++)
3219     for (y = BY1; y <= BY2; y++)
3220       DrawScreenField(x, y);
3221
3222   redraw_mask |= REDRAW_FIELD;
3223 }
3224
3225 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3226                     int tilesize)
3227 {
3228   int x,y;
3229
3230   for (x = 0; x < size_x; x++)
3231     for (y = 0; y < size_y; y++)
3232       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3233
3234   redraw_mask |= REDRAW_FIELD;
3235 }
3236
3237 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3238 {
3239   int x,y;
3240
3241   for (x = 0; x < size_x; x++)
3242     for (y = 0; y < size_y; y++)
3243       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3244
3245   redraw_mask |= REDRAW_FIELD;
3246 }
3247
3248 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3249 {
3250   boolean show_level_border = (BorderElement != EL_EMPTY);
3251   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3252   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3253   int tile_size = preview.tile_size;
3254   int preview_width  = preview.xsize * tile_size;
3255   int preview_height = preview.ysize * tile_size;
3256   int real_preview_xsize = MIN(level_xsize, preview.xsize);
3257   int real_preview_ysize = MIN(level_ysize, preview.ysize);
3258   int real_preview_width  = real_preview_xsize * tile_size;
3259   int real_preview_height = real_preview_ysize * tile_size;
3260   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3261   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3262   int x, y;
3263
3264   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3265     return;
3266
3267   DrawBackground(dst_x, dst_y, preview_width, preview_height);
3268
3269   dst_x += (preview_width  - real_preview_width)  / 2;
3270   dst_y += (preview_height - real_preview_height) / 2;
3271
3272   for (x = 0; x < real_preview_xsize; x++)
3273   {
3274     for (y = 0; y < real_preview_ysize; y++)
3275     {
3276       int lx = from_x + x + (show_level_border ? -1 : 0);
3277       int ly = from_y + y + (show_level_border ? -1 : 0);
3278       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3279                      getBorderElement(lx, ly));
3280
3281       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3282                          element, tile_size);
3283     }
3284   }
3285
3286   redraw_mask |= REDRAW_FIELD;
3287 }
3288
3289 #define MICROLABEL_EMPTY                0
3290 #define MICROLABEL_LEVEL_NAME           1
3291 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
3292 #define MICROLABEL_LEVEL_AUTHOR         3
3293 #define MICROLABEL_IMPORTED_FROM_HEAD   4
3294 #define MICROLABEL_IMPORTED_FROM        5
3295 #define MICROLABEL_IMPORTED_BY_HEAD     6
3296 #define MICROLABEL_IMPORTED_BY          7
3297
3298 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3299 {
3300   int max_text_width = SXSIZE;
3301   int font_width = getFontWidth(font_nr);
3302
3303   if (pos->align == ALIGN_CENTER)
3304     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3305   else if (pos->align == ALIGN_RIGHT)
3306     max_text_width = pos->x;
3307   else
3308     max_text_width = SXSIZE - pos->x;
3309
3310   return max_text_width / font_width;
3311 }
3312
3313 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3314 {
3315   char label_text[MAX_OUTPUT_LINESIZE + 1];
3316   int max_len_label_text;
3317   int font_nr = pos->font;
3318   int i;
3319
3320   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3321     return;
3322
3323   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3324       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3325       mode == MICROLABEL_IMPORTED_BY_HEAD)
3326     font_nr = pos->font_alt;
3327
3328   max_len_label_text = getMaxTextLength(pos, font_nr);
3329
3330   if (pos->size != -1)
3331     max_len_label_text = pos->size;
3332
3333   for (i = 0; i < max_len_label_text; i++)
3334     label_text[i] = ' ';
3335   label_text[max_len_label_text] = '\0';
3336
3337   if (strlen(label_text) > 0)
3338     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3339
3340   strncpy(label_text,
3341           (mode == MICROLABEL_LEVEL_NAME ? level.name :
3342            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3343            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3344            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3345            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3346            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3347            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3348           max_len_label_text);
3349   label_text[max_len_label_text] = '\0';
3350
3351   if (strlen(label_text) > 0)
3352     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3353
3354   redraw_mask |= REDRAW_FIELD;
3355 }
3356
3357 static void DrawPreviewLevelLabel(int mode)
3358 {
3359   DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3360 }
3361
3362 static void DrawPreviewLevelInfo(int mode)
3363 {
3364   if (mode == MICROLABEL_LEVEL_NAME)
3365     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3366   else if (mode == MICROLABEL_LEVEL_AUTHOR)
3367     DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3368 }
3369
3370 static void DrawPreviewLevelExt(boolean restart)
3371 {
3372   static unsigned int scroll_delay = 0;
3373   static unsigned int label_delay = 0;
3374   static int from_x, from_y, scroll_direction;
3375   static int label_state, label_counter;
3376   unsigned int scroll_delay_value = preview.step_delay;
3377   boolean show_level_border = (BorderElement != EL_EMPTY);
3378   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3379   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3380
3381   if (restart)
3382   {
3383     from_x = 0;
3384     from_y = 0;
3385
3386     if (preview.anim_mode == ANIM_CENTERED)
3387     {
3388       if (level_xsize > preview.xsize)
3389         from_x = (level_xsize - preview.xsize) / 2;
3390       if (level_ysize > preview.ysize)
3391         from_y = (level_ysize - preview.ysize) / 2;
3392     }
3393
3394     from_x += preview.xoffset;
3395     from_y += preview.yoffset;
3396
3397     scroll_direction = MV_RIGHT;
3398     label_state = 1;
3399     label_counter = 0;
3400
3401     DrawPreviewLevelPlayfield(from_x, from_y);
3402     DrawPreviewLevelLabel(label_state);
3403
3404     DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3405     DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3406
3407     // initialize delay counters
3408     DelayReached(&scroll_delay, 0);
3409     DelayReached(&label_delay, 0);
3410
3411     if (leveldir_current->name)
3412     {
3413       struct TextPosInfo *pos = &menu.main.text.level_info_1;
3414       char label_text[MAX_OUTPUT_LINESIZE + 1];
3415       int font_nr = pos->font;
3416       int max_len_label_text = getMaxTextLength(pos, font_nr);
3417
3418       if (pos->size != -1)
3419         max_len_label_text = pos->size;
3420
3421       strncpy(label_text, leveldir_current->name, max_len_label_text);
3422       label_text[max_len_label_text] = '\0';
3423
3424       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3425         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3426     }
3427
3428     return;
3429   }
3430
3431   // scroll preview level, if needed
3432   if (preview.anim_mode != ANIM_NONE &&
3433       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3434       DelayReached(&scroll_delay, scroll_delay_value))
3435   {
3436     switch (scroll_direction)
3437     {
3438       case MV_LEFT:
3439         if (from_x > 0)
3440         {
3441           from_x -= preview.step_offset;
3442           from_x = (from_x < 0 ? 0 : from_x);
3443         }
3444         else
3445           scroll_direction = MV_UP;
3446         break;
3447
3448       case MV_RIGHT:
3449         if (from_x < level_xsize - preview.xsize)
3450         {
3451           from_x += preview.step_offset;
3452           from_x = (from_x > level_xsize - preview.xsize ?
3453                     level_xsize - preview.xsize : from_x);
3454         }
3455         else
3456           scroll_direction = MV_DOWN;
3457         break;
3458
3459       case MV_UP:
3460         if (from_y > 0)
3461         {
3462           from_y -= preview.step_offset;
3463           from_y = (from_y < 0 ? 0 : from_y);
3464         }
3465         else
3466           scroll_direction = MV_RIGHT;
3467         break;
3468
3469       case MV_DOWN:
3470         if (from_y < level_ysize - preview.ysize)
3471         {
3472           from_y += preview.step_offset;
3473           from_y = (from_y > level_ysize - preview.ysize ?
3474                     level_ysize - preview.ysize : from_y);
3475         }
3476         else
3477           scroll_direction = MV_LEFT;
3478         break;
3479
3480       default:
3481         break;
3482     }
3483
3484     DrawPreviewLevelPlayfield(from_x, from_y);
3485   }
3486
3487   // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3488   // redraw micro level label, if needed
3489   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3490       !strEqual(level.author, ANONYMOUS_NAME) &&
3491       !strEqual(level.author, leveldir_current->name) &&
3492       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3493   {
3494     int max_label_counter = 23;
3495
3496     if (leveldir_current->imported_from != NULL &&
3497         strlen(leveldir_current->imported_from) > 0)
3498       max_label_counter += 14;
3499     if (leveldir_current->imported_by != NULL &&
3500         strlen(leveldir_current->imported_by) > 0)
3501       max_label_counter += 14;
3502
3503     label_counter = (label_counter + 1) % max_label_counter;
3504     label_state = (label_counter >= 0 && label_counter <= 7 ?
3505                    MICROLABEL_LEVEL_NAME :
3506                    label_counter >= 9 && label_counter <= 12 ?
3507                    MICROLABEL_LEVEL_AUTHOR_HEAD :
3508                    label_counter >= 14 && label_counter <= 21 ?
3509                    MICROLABEL_LEVEL_AUTHOR :
3510                    label_counter >= 23 && label_counter <= 26 ?
3511                    MICROLABEL_IMPORTED_FROM_HEAD :
3512                    label_counter >= 28 && label_counter <= 35 ?
3513                    MICROLABEL_IMPORTED_FROM :
3514                    label_counter >= 37 && label_counter <= 40 ?
3515                    MICROLABEL_IMPORTED_BY_HEAD :
3516                    label_counter >= 42 && label_counter <= 49 ?
3517                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3518
3519     if (leveldir_current->imported_from == NULL &&
3520         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3521          label_state == MICROLABEL_IMPORTED_FROM))
3522       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3523                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3524
3525     DrawPreviewLevelLabel(label_state);
3526   }
3527 }
3528
3529 void DrawPreviewPlayers(void)
3530 {
3531   if (game_status != GAME_MODE_MAIN)
3532     return;
3533
3534   // do not draw preview players if level preview redefined, but players aren't
3535   if (preview.redefined && !menu.main.preview_players.redefined)
3536     return;
3537
3538   boolean player_found[MAX_PLAYERS];
3539   int num_players = 0;
3540   int i, x, y;
3541
3542   for (i = 0; i < MAX_PLAYERS; i++)
3543     player_found[i] = FALSE;
3544
3545   // check which players can be found in the level (simple approach)
3546   for (x = 0; x < lev_fieldx; x++)
3547   {
3548     for (y = 0; y < lev_fieldy; y++)
3549     {
3550       int element = level.field[x][y];
3551
3552       if (ELEM_IS_PLAYER(element))
3553       {
3554         int player_nr = GET_PLAYER_NR(element);
3555
3556         player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3557
3558         if (!player_found[player_nr])
3559           num_players++;
3560
3561         player_found[player_nr] = TRUE;
3562       }
3563     }
3564   }
3565
3566   struct TextPosInfo *pos = &menu.main.preview_players;
3567   int tile_size = pos->tile_size;
3568   int border_size = pos->border_size;
3569   int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3570   int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3571   int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3572   int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3573   int max_players_width  = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3574   int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3575   int all_players_width  = (num_players - 1) * player_xoffset + tile_size;
3576   int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3577   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3578   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3579   int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width,  pos->align);
3580   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3581
3582   // clear area in which the players will be drawn
3583   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3584                              max_players_width, max_players_height);
3585
3586   if (!network.enabled && !setup.team_mode)
3587     return;
3588
3589   // only draw players if level is suited for team mode
3590   if (num_players < 2)
3591     return;
3592
3593   // draw all players that were found in the level
3594   for (i = 0; i < MAX_PLAYERS; i++)
3595   {
3596     if (player_found[i])
3597     {
3598       int graphic = el2img(EL_PLAYER_1 + i);
3599
3600       DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3601
3602       xpos += player_xoffset;
3603       ypos += player_yoffset;
3604     }
3605   }
3606 }
3607
3608 void DrawPreviewLevelInitial(void)
3609 {
3610   DrawPreviewLevelExt(TRUE);
3611   DrawPreviewPlayers();
3612 }
3613
3614 void DrawPreviewLevelAnimation(void)
3615 {
3616   DrawPreviewLevelExt(FALSE);
3617 }
3618
3619 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3620                               int border_size, int font_nr)
3621 {
3622   int graphic = el2img(EL_PLAYER_1 + player_nr);
3623   int font_height = getFontHeight(font_nr);
3624   int player_height = MAX(tile_size, font_height);
3625   int xoffset_text = tile_size + border_size;
3626   int yoffset_text    = (player_height - font_height) / 2;
3627   int yoffset_graphic = (player_height - tile_size) / 2;
3628   char *player_name = getNetworkPlayerName(player_nr + 1);
3629
3630   DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3631                               tile_size);
3632   DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3633 }
3634
3635 static void DrawNetworkPlayersExt(boolean force)
3636 {
3637   if (game_status != GAME_MODE_MAIN)
3638     return;
3639
3640   if (!network.connected && !force)
3641     return;
3642
3643   // do not draw network players if level preview redefined, but players aren't
3644   if (preview.redefined && !menu.main.network_players.redefined)
3645     return;
3646
3647   int num_players = 0;
3648   int i;
3649
3650   for (i = 0; i < MAX_PLAYERS; i++)
3651     if (stored_player[i].connected_network)
3652       num_players++;
3653
3654   struct TextPosInfo *pos = &menu.main.network_players;
3655   int tile_size = pos->tile_size;
3656   int border_size = pos->border_size;
3657   int xoffset_text = tile_size + border_size;
3658   int font_nr = pos->font;
3659   int font_width = getFontWidth(font_nr);
3660   int font_height = getFontHeight(font_nr);
3661   int player_height = MAX(tile_size, font_height);
3662   int player_yoffset = player_height + border_size;
3663   int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3664   int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3665   int all_players_height = num_players * player_yoffset - border_size;
3666   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3667   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3668   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3669
3670   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3671                              max_players_width, max_players_height);
3672
3673   // first draw local network player ...
3674   for (i = 0; i < MAX_PLAYERS; i++)
3675   {
3676     if (stored_player[i].connected_network &&
3677         stored_player[i].connected_locally)
3678     {
3679       char *player_name = getNetworkPlayerName(i + 1);
3680       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3681       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3682
3683       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3684
3685       ypos += player_yoffset;
3686     }
3687   }
3688
3689   // ... then draw all other network players
3690   for (i = 0; i < MAX_PLAYERS; i++)
3691   {
3692     if (stored_player[i].connected_network &&
3693         !stored_player[i].connected_locally)
3694     {
3695       char *player_name = getNetworkPlayerName(i + 1);
3696       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3697       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3698
3699       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3700
3701       ypos += player_yoffset;
3702     }
3703   }
3704 }
3705
3706 void DrawNetworkPlayers(void)
3707 {
3708   DrawNetworkPlayersExt(FALSE);
3709 }
3710
3711 void ClearNetworkPlayers(void)
3712 {
3713   DrawNetworkPlayersExt(TRUE);
3714 }
3715
3716 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3717                                     int graphic, int sync_frame,
3718                                     int mask_mode)
3719 {
3720   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3721
3722   if (mask_mode == USE_MASKING)
3723     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3724   else
3725     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3726 }
3727
3728 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3729                                   int graphic, int sync_frame, int mask_mode)
3730 {
3731   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3732
3733   if (mask_mode == USE_MASKING)
3734     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3735   else
3736     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3737 }
3738
3739 static void DrawGraphicAnimation(int x, int y, int graphic)
3740 {
3741   int lx = LEVELX(x), ly = LEVELY(y);
3742
3743   if (!IN_SCR_FIELD(x, y))
3744     return;
3745
3746   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3747                           graphic, GfxFrame[lx][ly], NO_MASKING);
3748
3749   MarkTileDirty(x, y);
3750 }
3751
3752 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3753 {
3754   int lx = LEVELX(x), ly = LEVELY(y);
3755
3756   if (!IN_SCR_FIELD(x, y))
3757     return;
3758
3759   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3760                           graphic, GfxFrame[lx][ly], NO_MASKING);
3761   MarkTileDirty(x, y);
3762 }
3763
3764 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3765 {
3766   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3767 }
3768
3769 void DrawLevelElementAnimation(int x, int y, int element)
3770 {
3771   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3772
3773   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3774 }
3775
3776 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3777 {
3778   int sx = SCREENX(x), sy = SCREENY(y);
3779
3780   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3781     return;
3782
3783   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3784     return;
3785
3786   DrawGraphicAnimation(sx, sy, graphic);
3787
3788 #if 1
3789   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3790     DrawLevelFieldCrumbled(x, y);
3791 #else
3792   if (GFX_CRUMBLED(Feld[x][y]))
3793     DrawLevelFieldCrumbled(x, y);
3794 #endif
3795 }
3796
3797 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3798 {
3799   int sx = SCREENX(x), sy = SCREENY(y);
3800   int graphic;
3801
3802   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3803     return;
3804
3805   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3806
3807   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3808     return;
3809
3810   DrawGraphicAnimation(sx, sy, graphic);
3811
3812   if (GFX_CRUMBLED(element))
3813     DrawLevelFieldCrumbled(x, y);
3814 }
3815
3816 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3817 {
3818   if (player->use_murphy)
3819   {
3820     // this works only because currently only one player can be "murphy" ...
3821     static int last_horizontal_dir = MV_LEFT;
3822     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3823
3824     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3825       last_horizontal_dir = move_dir;
3826
3827     if (graphic == IMG_SP_MURPHY)       // undefined => use special graphic
3828     {
3829       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3830
3831       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3832     }
3833
3834     return graphic;
3835   }
3836   else
3837     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3838 }
3839
3840 static boolean equalGraphics(int graphic1, int graphic2)
3841 {
3842   struct GraphicInfo *g1 = &graphic_info[graphic1];
3843   struct GraphicInfo *g2 = &graphic_info[graphic2];
3844
3845   return (g1->bitmap      == g2->bitmap &&
3846           g1->src_x       == g2->src_x &&
3847           g1->src_y       == g2->src_y &&
3848           g1->anim_frames == g2->anim_frames &&
3849           g1->anim_delay  == g2->anim_delay &&
3850           g1->anim_mode   == g2->anim_mode);
3851 }
3852
3853 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3854
3855 enum
3856 {
3857   DRAW_PLAYER_STAGE_INIT = 0,
3858   DRAW_PLAYER_STAGE_LAST_FIELD,
3859   DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3860 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3861   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3862   DRAW_PLAYER_STAGE_PLAYER,
3863 #else
3864   DRAW_PLAYER_STAGE_PLAYER,
3865   DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3866 #endif
3867   DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3868   DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3869
3870   NUM_DRAW_PLAYER_STAGES
3871 };
3872
3873 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3874 {
3875   static int static_last_player_graphic[MAX_PLAYERS];
3876   static int static_last_player_frame[MAX_PLAYERS];
3877   static boolean static_player_is_opaque[MAX_PLAYERS];
3878   static boolean draw_player[MAX_PLAYERS];
3879   int pnr = player->index_nr;
3880
3881   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3882   {
3883     static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3884     static_last_player_frame[pnr] = player->Frame;
3885     static_player_is_opaque[pnr] = FALSE;
3886
3887     draw_player[pnr] = TRUE;
3888   }
3889
3890   if (!draw_player[pnr])
3891     return;
3892
3893 #if DEBUG
3894   if (!IN_LEV_FIELD(player->jx, player->jy))
3895   {
3896     printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3897     printf("DrawPlayerField(): This should never happen!\n");
3898
3899     draw_player[pnr] = FALSE;
3900
3901     return;
3902   }
3903 #endif
3904
3905   int last_player_graphic  = static_last_player_graphic[pnr];
3906   int last_player_frame    = static_last_player_frame[pnr];
3907   boolean player_is_opaque = static_player_is_opaque[pnr];
3908
3909   int jx = player->jx;
3910   int jy = player->jy;
3911   int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3912   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3913   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
3914   int last_jx = (player->is_moving ? jx - dx : jx);
3915   int last_jy = (player->is_moving ? jy - dy : jy);
3916   int next_jx = jx + dx;
3917   int next_jy = jy + dy;
3918   boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3919   int sx = SCREENX(jx);
3920   int sy = SCREENY(jy);
3921   int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3922   int syy = (move_dir == MV_UP   || move_dir == MV_DOWN  ? player->GfxPos : 0);
3923   int element = Feld[jx][jy];
3924   int last_element = Feld[last_jx][last_jy];
3925   int action = (player->is_pushing    ? ACTION_PUSHING         :
3926                 player->is_digging    ? ACTION_DIGGING         :
3927                 player->is_collecting ? ACTION_COLLECTING      :
3928                 player->is_moving     ? ACTION_MOVING          :
3929                 player->is_snapping   ? ACTION_SNAPPING        :
3930                 player->is_dropping   ? ACTION_DROPPING        :
3931                 player->is_waiting    ? player->action_waiting :
3932                 ACTION_DEFAULT);
3933
3934   if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3935   {
3936     // ------------------------------------------------------------------------
3937     // initialize drawing the player
3938     // ------------------------------------------------------------------------
3939
3940     draw_player[pnr] = FALSE;
3941
3942     // GfxElement[][] is set to the element the player is digging or collecting;
3943     // remove also for off-screen player if the player is not moving anymore
3944     if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3945       GfxElement[jx][jy] = EL_UNDEFINED;
3946
3947     if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3948       return;
3949
3950     if (element == EL_EXPLOSION)
3951       return;
3952
3953     InitPlayerGfxAnimation(player, action, move_dir);
3954
3955     draw_player[pnr] = TRUE;
3956   }
3957   else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3958   {
3959     // ------------------------------------------------------------------------
3960     // draw things in the field the player is leaving, if needed
3961     // ------------------------------------------------------------------------
3962
3963     if (!IN_SCR_FIELD(sx, sy))
3964       draw_player[pnr] = FALSE;
3965
3966     if (!player->is_moving)
3967       return;
3968
3969     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3970     {