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