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