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