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