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