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