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