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