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