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