removed unused setup option 'soft_scrolling'
[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_FIELDBUFFER)
237   {
238     FX = 2 * TILEX_VAR;
239     FY = 2 * TILEY_VAR;
240     BX1 = -2;
241     BY1 = -2;
242     BX2 = SCR_FIELDX + 1;
243     BY2 = SCR_FIELDY + 1;
244     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   BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
296 }
297
298 void DrawMaskedBorder_FIELD()
299 {
300   if (global.border_status >= GAME_MODE_TITLE &&
301       global.border_status <= GAME_MODE_PLAYING &&
302       border.draw_masked[global.border_status])
303     DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
304 }
305
306 void DrawMaskedBorder_DOOR_1()
307 {
308   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
309       (global.border_status != GAME_MODE_EDITOR ||
310        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
311     DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
312 }
313
314 void DrawMaskedBorder_DOOR_2()
315 {
316   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
317       global.border_status != GAME_MODE_EDITOR)
318     DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
319 }
320
321 void DrawMaskedBorder_DOOR_3()
322 {
323   /* currently not available */
324 }
325
326 void DrawMaskedBorder_ALL()
327 {
328   DrawMaskedBorder_FIELD();
329   DrawMaskedBorder_DOOR_1();
330   DrawMaskedBorder_DOOR_2();
331   DrawMaskedBorder_DOOR_3();
332 }
333
334 void DrawMaskedBorder(int redraw_mask)
335 {
336   /* never draw masked screen borders on borderless screens */
337   if (effectiveGameStatus() == GAME_MODE_LOADING ||
338       effectiveGameStatus() == GAME_MODE_TITLE)
339     return;
340
341   /* never draw masked screen borders when displaying request outside door */
342   if (effectiveGameStatus() == GAME_MODE_PSEUDO_DOOR &&
343       global.use_envelope_request)
344     return;
345
346   if (redraw_mask & REDRAW_ALL)
347     DrawMaskedBorder_ALL();
348   else
349   {
350     if (redraw_mask & REDRAW_FIELD)
351       DrawMaskedBorder_FIELD();
352     if (redraw_mask & REDRAW_DOOR_1)
353       DrawMaskedBorder_DOOR_1();
354     if (redraw_mask & REDRAW_DOOR_2)
355       DrawMaskedBorder_DOOR_2();
356     if (redraw_mask & REDRAW_DOOR_3)
357       DrawMaskedBorder_DOOR_3();
358   }
359 }
360
361 static void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
362 {
363   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
364   int fx = FX, fy = FY;
365   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
366   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
367
368   int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
369   int dy = (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
370   int dx_var = dx * TILESIZE_VAR / TILESIZE;
371   int dy_var = dy * TILESIZE_VAR / TILESIZE;
372   int ffx, ffy;
373
374   ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
375   ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
376
377   if (EVEN(SCR_FIELDX))
378   {
379     if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
380       fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
381     else
382       fx += (dx_var > 0 ? TILEX_VAR : 0);
383   }
384   else
385   {
386     fx += dx_var;
387   }
388
389   if (EVEN(SCR_FIELDY))
390   {
391     if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
392       fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
393     else
394       fy += (dy_var > 0 ? TILEY_VAR : 0);
395   }
396   else
397   {
398     fy += dy_var;
399   }
400
401   if (full_lev_fieldx <= SCR_FIELDX)
402   {
403     if (EVEN(SCR_FIELDX))
404       fx = 2 * TILEX_VAR - (ODD(lev_fieldx)  ? TILEX_VAR / 2 : 0);
405     else
406       fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
407   }
408
409   if (full_lev_fieldy <= SCR_FIELDY)
410   {
411     if (EVEN(SCR_FIELDY))
412       fy = 2 * TILEY_VAR - (ODD(lev_fieldy)  ? TILEY_VAR / 2 : 0);
413     else
414       fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
415   }
416
417   if (border.draw_masked[GAME_MODE_PLAYING])
418   {
419     if (buffer != backbuffer)
420     {
421       /* copy playfield buffer to backbuffer to add masked border */
422       BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
423       DrawMaskedBorder(REDRAW_FIELD);
424     }
425
426     BlitBitmap(backbuffer, target_bitmap,
427                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
428                REAL_SX, REAL_SY);
429   }
430   else
431   {
432     BlitBitmap(buffer, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
433   }
434 }
435
436 void BlitScreenToBitmap(Bitmap *target_bitmap)
437 {
438   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
439     BlitScreenToBitmap_EM(target_bitmap);
440   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
441     BlitScreenToBitmap_SP(target_bitmap);
442   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
443     BlitScreenToBitmap_RND(target_bitmap);
444 }
445
446 void BackToFront()
447 {
448   int x, y;
449   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
450
451   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
452     redraw_mask |= REDRAW_FIELD;
453
454 #if 0
455   // never redraw single tiles, always redraw the whole field
456   // (redrawing single tiles up to a certain threshold was faster on old,
457   // now legacy graphics, but slows things down on modern graphics now)
458   // UPDATE: this is now globally defined by value of REDRAWTILES_THRESHOLD
459   if (redraw_mask & REDRAW_TILES)
460     redraw_mask |= REDRAW_FIELD;
461 #endif
462
463 #if 0
464   /* !!! TEST ONLY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
465   /* (force full redraw) */
466   if (game_status == GAME_MODE_PLAYING)
467     redraw_mask |= REDRAW_FIELD;
468 #endif
469
470   if (redraw_mask & REDRAW_FIELD)
471     redraw_mask &= ~REDRAW_TILES;
472
473   if (redraw_mask == REDRAW_NONE)
474     return;
475
476 #if 0
477   printf("::: ");
478   if (redraw_mask & REDRAW_ALL)
479     printf("[REDRAW_ALL]");
480   if (redraw_mask & REDRAW_FIELD)
481     printf("[REDRAW_FIELD]");
482   if (redraw_mask & REDRAW_TILES)
483     printf("[REDRAW_TILES]");
484   if (redraw_mask & REDRAW_DOOR_1)
485     printf("[REDRAW_DOOR_1]");
486   if (redraw_mask & REDRAW_DOOR_2)
487     printf("[REDRAW_DOOR_2]");
488   if (redraw_mask & REDRAW_FROM_BACKBUFFER)
489     printf("[REDRAW_FROM_BACKBUFFER]");
490   printf(" [%d]\n", FrameCounter);
491 #endif
492
493   if (redraw_mask & REDRAW_TILES &&
494       game_status == GAME_MODE_PLAYING &&
495       border.draw_masked[GAME_MODE_PLAYING])
496     redraw_mask |= REDRAW_FIELD;
497
498   if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
499   {
500     static boolean last_frame_skipped = FALSE;
501     boolean skip_even_when_not_scrolling = TRUE;
502     boolean just_scrolling = (ScreenMovDir != 0);
503     boolean verbose = FALSE;
504
505     if (global.fps_slowdown_factor > 1 &&
506         (FrameCounter % global.fps_slowdown_factor) &&
507         (just_scrolling || skip_even_when_not_scrolling))
508     {
509       redraw_mask &= ~REDRAW_MAIN;
510
511       last_frame_skipped = TRUE;
512
513       if (verbose)
514         printf("FRAME SKIPPED\n");
515     }
516     else
517     {
518       if (last_frame_skipped)
519         redraw_mask |= REDRAW_FIELD;
520
521       last_frame_skipped = FALSE;
522
523       if (verbose)
524         printf("frame not skipped\n");
525     }
526   }
527
528   /* synchronize X11 graphics at this point; if we would synchronize the
529      display immediately after the buffer switching (after the XFlush),
530      this could mean that we have to wait for the graphics to complete,
531      although we could go on doing calculations for the next frame */
532
533   /* SyncDisplay(); */
534
535   /* never draw masked border to backbuffer when using playfield buffer */
536   if (game_status != GAME_MODE_PLAYING ||
537       redraw_mask & REDRAW_FROM_BACKBUFFER ||
538       buffer == backbuffer)
539     DrawMaskedBorder(redraw_mask);
540   else
541     DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
542
543   if (redraw_mask & REDRAW_ALL)
544   {
545     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
546
547     redraw_mask = REDRAW_NONE;
548   }
549
550   if (redraw_mask & REDRAW_FIELD)
551   {
552     if (game_status != GAME_MODE_PLAYING ||
553         redraw_mask & REDRAW_FROM_BACKBUFFER)
554     {
555       BlitBitmap(backbuffer, window,
556                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
557     }
558     else
559     {
560       BlitScreenToBitmap_RND(window);
561     }
562
563     redraw_mask &= ~REDRAW_MAIN;
564   }
565
566   if (redraw_mask & REDRAW_DOORS)
567   {
568     if (redraw_mask & REDRAW_DOOR_1)
569       BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
570
571     if (redraw_mask & REDRAW_DOOR_2)
572       BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
573
574     if (redraw_mask & REDRAW_DOOR_3)
575       BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
576
577     redraw_mask &= ~REDRAW_DOORS;
578   }
579
580   if (redraw_mask & REDRAW_MICROLEVEL)
581   {
582     BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
583                SX, SY + 10 * TILEY);
584
585     redraw_mask &= ~REDRAW_MICROLEVEL;
586   }
587
588   if (redraw_mask & REDRAW_TILES)
589   {
590     int sx = SX;
591     int sy = SY;
592
593     int dx = 0, dy = 0;
594     int dx_var = dx * TILESIZE_VAR / TILESIZE;
595     int dy_var = dy * TILESIZE_VAR / TILESIZE;
596     int ffx, ffy;
597     int fx = FX, fy = FY;
598
599     int scr_fieldx = SCR_FIELDX + (EVEN(SCR_FIELDX) ? 2 : 0);
600     int scr_fieldy = SCR_FIELDY + (EVEN(SCR_FIELDY) ? 2 : 0);
601
602     InitGfxClipRegion(TRUE, SX, SY, SXSIZE, SYSIZE);
603
604     ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
605     ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
606
607     if (EVEN(SCR_FIELDX))
608     {
609       if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
610       {
611         fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
612
613         if (fx % TILEX_VAR)
614           sx -= TILEX_VAR / 2;
615         else
616           sx -= TILEX_VAR;
617       }
618       else
619       {
620         fx += (dx_var > 0 ? TILEX_VAR : 0);
621       }
622     }
623
624     if (EVEN(SCR_FIELDY))
625     {
626       if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
627       {
628         fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
629
630         if (fy % TILEY_VAR)
631           sy -= TILEY_VAR / 2;
632         else
633           sy -= TILEY_VAR;
634       }
635       else
636       {
637         fy += (dy_var > 0 ? TILEY_VAR : 0);
638       }
639     }
640
641     for (x = 0; x < scr_fieldx; x++)
642       for (y = 0 ; y < scr_fieldy; y++)
643         if (redraw[redraw_x1 + x][redraw_y1 + y])
644           BlitBitmap(buffer, window,
645                      FX + x * TILEX_VAR, FY + y * TILEY_VAR,
646                      TILEX_VAR, TILEY_VAR,
647                      sx + x * TILEX_VAR, sy + y * TILEY_VAR);
648
649     InitGfxClipRegion(FALSE, -1, -1, -1, -1);
650   }
651
652   if (redraw_mask & REDRAW_FPS)         /* display frames per second */
653   {
654     char text[100];
655     char info1[100];
656
657     sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
658     if (!global.fps_slowdown)
659       info1[0] = '\0';
660
661     sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
662
663     DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
664   }
665
666   for (x = 0; x < MAX_BUF_XSIZE; x++)
667     for (y = 0; y < MAX_BUF_YSIZE; y++)
668       redraw[x][y] = 0;
669   redraw_tiles = 0;
670   redraw_mask = REDRAW_NONE;
671 }
672
673 static void FadeCrossSaveBackbuffer()
674 {
675   BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
676 }
677
678 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
679 {
680   static int fade_type_skip = FADE_TYPE_NONE;
681   void (*draw_border_function)(void) = NULL;
682   Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
683   int x, y, width, height;
684   int fade_delay, post_delay;
685
686   if (fade_type == FADE_TYPE_FADE_OUT)
687   {
688     if (fade_type_skip != FADE_TYPE_NONE)
689     {
690       /* skip all fade operations until specified fade operation */
691       if (fade_type & fade_type_skip)
692         fade_type_skip = FADE_TYPE_NONE;
693
694       return;
695     }
696
697     if (fading.fade_mode & FADE_TYPE_TRANSFORM)
698     {
699       FadeCrossSaveBackbuffer();
700
701       return;
702     }
703   }
704
705   redraw_mask |= fade_mask;
706
707   if (fade_type == FADE_TYPE_SKIP)
708   {
709     fade_type_skip = fade_mode;
710
711     return;
712   }
713
714   fade_delay = fading.fade_delay;
715   post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
716
717   if (fade_type_skip != FADE_TYPE_NONE)
718   {
719     /* skip all fade operations until specified fade operation */
720     if (fade_type & fade_type_skip)
721       fade_type_skip = FADE_TYPE_NONE;
722
723     fade_delay = 0;
724   }
725
726   if (global.autoplay_leveldir)
727   {
728     return;
729   }
730
731   if (fade_mask == REDRAW_FIELD)
732   {
733     x = REAL_SX;
734     y = REAL_SY;
735     width  = FULL_SXSIZE;
736     height = FULL_SYSIZE;
737
738     if (border.draw_masked_when_fading)
739       draw_border_function = DrawMaskedBorder_FIELD;    /* update when fading */
740     else
741       DrawMaskedBorder_FIELD();                         /* draw once */
742   }
743   else          /* REDRAW_ALL */
744   {
745     x = 0;
746     y = 0;
747     width  = WIN_XSIZE;
748     height = WIN_YSIZE;
749   }
750
751   if (!setup.fade_screens ||
752       fade_delay == 0 ||
753       fading.fade_mode == FADE_MODE_NONE)
754   {
755     if (fade_mode == FADE_MODE_FADE_OUT)
756       return;
757
758     BlitBitmap(backbuffer, window, x, y, width, height, x, y);
759
760     redraw_mask &= ~fade_mask;
761
762     return;
763   }
764
765   FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
766                 draw_border_function);
767
768   redraw_mask &= ~fade_mask;
769 }
770
771 void FadeIn(int fade_mask)
772 {
773   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
774     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
775   else
776     FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
777 }
778
779 void FadeOut(int fade_mask)
780 {
781   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
782     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
783   else
784     FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
785
786   global.border_status = game_status;
787 }
788
789 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
790 {
791   static struct TitleFadingInfo fading_leave_stored;
792
793   if (set)
794     fading_leave_stored = fading_leave;
795   else
796     fading = fading_leave_stored;
797 }
798
799 void FadeSetEnterMenu()
800 {
801   fading = menu.enter_menu;
802
803   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
804 }
805
806 void FadeSetLeaveMenu()
807 {
808   fading = menu.leave_menu;
809
810   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
811 }
812
813 void FadeSetEnterScreen()
814 {
815   fading = menu.enter_screen[game_status];
816
817   FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);       /* store */
818 }
819
820 void FadeSetNextScreen()
821 {
822   fading = menu.next_screen;
823
824   // (do not overwrite fade mode set by FadeSetEnterScreen)
825   // FadeSetLeaveNext(fading, TRUE);    /* (keep same fade mode) */
826 }
827
828 void FadeSetLeaveScreen()
829 {
830   FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);      /* recall */
831 }
832
833 void FadeSetFromType(int type)
834 {
835   if (type & TYPE_ENTER_SCREEN)
836     FadeSetEnterScreen();
837   else if (type & TYPE_ENTER)
838     FadeSetEnterMenu();
839   else if (type & TYPE_LEAVE)
840     FadeSetLeaveMenu();
841 }
842
843 void FadeSetDisabled()
844 {
845   static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
846
847   fading = fading_none;
848 }
849
850 void FadeSkipNextFadeIn()
851 {
852   FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
853 }
854
855 void FadeSkipNextFadeOut()
856 {
857   FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
858 }
859
860 void SetWindowBackgroundImageIfDefined(int graphic)
861 {
862   if (graphic_info[graphic].bitmap)
863     SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
864 }
865
866 void SetMainBackgroundImageIfDefined(int graphic)
867 {
868   if (graphic_info[graphic].bitmap)
869     SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
870 }
871
872 void SetDoorBackgroundImageIfDefined(int graphic)
873 {
874   if (graphic_info[graphic].bitmap)
875     SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
876 }
877
878 void SetWindowBackgroundImage(int graphic)
879 {
880   SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
881                             graphic_info[graphic].bitmap ?
882                             graphic_info[graphic].bitmap :
883                             graphic_info[IMG_BACKGROUND].bitmap);
884 }
885
886 void SetMainBackgroundImage(int graphic)
887 {
888   SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
889                           graphic_info[graphic].bitmap ?
890                           graphic_info[graphic].bitmap :
891                           graphic_info[IMG_BACKGROUND].bitmap);
892 }
893
894 void SetDoorBackgroundImage(int graphic)
895 {
896   SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
897                           graphic_info[graphic].bitmap ?
898                           graphic_info[graphic].bitmap :
899                           graphic_info[IMG_BACKGROUND].bitmap);
900 }
901
902 void SetPanelBackground()
903 {
904   struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
905
906   BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
907                   gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
908
909   SetDoorBackgroundBitmap(bitmap_db_panel);
910 }
911
912 void DrawBackground(int x, int y, int width, int height)
913 {
914   /* "drawto" might still point to playfield buffer here (hall of fame) */
915   ClearRectangleOnBackground(backbuffer, x, y, width, height);
916
917   if (IN_GFX_FIELD_FULL(x, y))
918     redraw_mask |= REDRAW_FIELD;
919   else if (IN_GFX_DOOR_1(x, y))
920     redraw_mask |= REDRAW_DOOR_1;
921   else if (IN_GFX_DOOR_2(x, y))
922     redraw_mask |= REDRAW_DOOR_2;
923   else if (IN_GFX_DOOR_3(x, y))
924     redraw_mask |= REDRAW_DOOR_3;
925 }
926
927 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
928 {
929   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
930
931   if (font->bitmap == NULL)
932     return;
933
934   DrawBackground(x, y, width, height);
935 }
936
937 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
938 {
939   struct GraphicInfo *g = &graphic_info[graphic];
940
941   if (g->bitmap == NULL)
942     return;
943
944   DrawBackground(x, y, width, height);
945 }
946
947 void ClearField()
948 {
949   /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
950   /* (when entering hall of fame after playing) */
951   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
952
953   /* !!! maybe this should be done before clearing the background !!! */
954   if (game_status == GAME_MODE_PLAYING)
955   {
956     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
957     SetDrawtoField(DRAW_FIELDBUFFER);
958   }
959   else
960     SetDrawtoField(DRAW_BACKBUFFER);
961 }
962
963 void MarkTileDirty(int x, int y)
964 {
965   int xx = redraw_x1 + x;
966   int yy = redraw_y1 + y;
967
968   if (!redraw[xx][yy])
969     redraw_tiles++;
970
971   redraw[xx][yy] = TRUE;
972   redraw_mask |= REDRAW_TILES;
973 }
974
975 void SetBorderElement()
976 {
977   int x, y;
978
979   BorderElement = EL_EMPTY;
980
981   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
982   {
983     for (x = 0; x < lev_fieldx; x++)
984     {
985       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
986         BorderElement = EL_STEELWALL;
987
988       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
989         x = lev_fieldx - 2;
990     }
991   }
992 }
993
994 void FloodFillLevel(int from_x, int from_y, int fill_element,
995                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
996                     int max_fieldx, int max_fieldy)
997 {
998   int i,x,y;
999   int old_element;
1000   static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1001   static int safety = 0;
1002
1003   /* check if starting field still has the desired content */
1004   if (field[from_x][from_y] == fill_element)
1005     return;
1006
1007   safety++;
1008
1009   if (safety > max_fieldx * max_fieldy)
1010     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1011
1012   old_element = field[from_x][from_y];
1013   field[from_x][from_y] = fill_element;
1014
1015   for (i = 0; i < 4; i++)
1016   {
1017     x = from_x + check[i][0];
1018     y = from_y + check[i][1];
1019
1020     if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1021       FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1022   }
1023
1024   safety--;
1025 }
1026
1027 void SetRandomAnimationValue(int x, int y)
1028 {
1029   gfx.anim_random_frame = GfxRandom[x][y];
1030 }
1031
1032 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
1033 {
1034   /* animation synchronized with global frame counter, not move position */
1035   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1036     sync_frame = FrameCounter;
1037
1038   return getAnimationFrame(graphic_info[graphic].anim_frames,
1039                            graphic_info[graphic].anim_delay,
1040                            graphic_info[graphic].anim_mode,
1041                            graphic_info[graphic].anim_start_frame,
1042                            sync_frame);
1043 }
1044
1045 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1046                               Bitmap **bitmap, int *x, int *y,
1047                               boolean get_backside)
1048 {
1049   struct GraphicInfo *g = &graphic_info[graphic];
1050   Bitmap *src_bitmap = g->bitmap;
1051   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1052   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1053   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1054
1055   // if no in-game graphics defined, always use standard graphic size
1056   if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1057     tilesize = TILESIZE;
1058
1059   if (tilesize == gfx.standard_tile_size)
1060     src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1061   else if (tilesize == game.tile_size)
1062     src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1063   else
1064     src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1065
1066   if (g->offset_y == 0)         /* frames are ordered horizontally */
1067   {
1068     int max_width = g->anim_frames_per_line * g->width;
1069     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1070
1071     src_x = pos % max_width;
1072     src_y = src_y % g->height + pos / max_width * g->height;
1073   }
1074   else if (g->offset_x == 0)    /* frames are ordered vertically */
1075   {
1076     int max_height = g->anim_frames_per_line * g->height;
1077     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1078
1079     src_x = src_x % g->width + pos / max_height * g->width;
1080     src_y = pos % max_height;
1081   }
1082   else                          /* frames are ordered diagonally */
1083   {
1084     src_x = src_x + frame * g->offset_x;
1085     src_y = src_y + frame * g->offset_y;
1086   }
1087
1088   *bitmap = src_bitmap;
1089   *x = src_x * tilesize / TILESIZE;
1090   *y = src_y * tilesize / TILESIZE;
1091 }
1092
1093 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1094                               int *x, int *y, boolean get_backside)
1095 {
1096   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1097                            get_backside);
1098 }
1099
1100 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1101                            Bitmap **bitmap, int *x, int *y)
1102 {
1103   getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1104 }
1105
1106 void getFixedGraphicSource(int graphic, int frame,
1107                            Bitmap **bitmap, int *x, int *y)
1108 {
1109   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1110 }
1111
1112 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1113 {
1114   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1115 }
1116
1117 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1118                                 int *x, int *y, boolean get_backside)
1119 {
1120   struct GraphicInfo *g = &graphic_info[graphic];
1121   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1122   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1123
1124   if (TILESIZE_VAR != TILESIZE)
1125     return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1126                                     get_backside);
1127
1128   *bitmap = g->bitmap;
1129
1130   if (g->offset_y == 0)         /* frames are ordered horizontally */
1131   {
1132     int max_width = g->anim_frames_per_line * g->width;
1133     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1134
1135     *x = pos % max_width;
1136     *y = src_y % g->height + pos / max_width * g->height;
1137   }
1138   else if (g->offset_x == 0)    /* frames are ordered vertically */
1139   {
1140     int max_height = g->anim_frames_per_line * g->height;
1141     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1142
1143     *x = src_x % g->width + pos / max_height * g->width;
1144     *y = pos % max_height;
1145   }
1146   else                          /* frames are ordered diagonally */
1147   {
1148     *x = src_x + frame * g->offset_x;
1149     *y = src_y + frame * g->offset_y;
1150   }
1151 }
1152
1153 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1154 {
1155   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1156 }
1157
1158 void DrawGraphic(int x, int y, int graphic, int frame)
1159 {
1160 #if DEBUG
1161   if (!IN_SCR_FIELD(x, y))
1162   {
1163     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1164     printf("DrawGraphic(): This should never happen!\n");
1165     return;
1166   }
1167 #endif
1168
1169   DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1170                  frame);
1171
1172   MarkTileDirty(x, y);
1173 }
1174
1175 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1176 {
1177 #if DEBUG
1178   if (!IN_SCR_FIELD(x, y))
1179   {
1180     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1181     printf("DrawGraphic(): This should never happen!\n");
1182     return;
1183   }
1184 #endif
1185
1186   DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1187                       frame);
1188   MarkTileDirty(x, y);
1189 }
1190
1191 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1192                     int frame)
1193 {
1194   Bitmap *src_bitmap;
1195   int src_x, src_y;
1196
1197   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1198
1199   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1200 }
1201
1202 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1203                          int frame)
1204 {
1205   Bitmap *src_bitmap;
1206   int src_x, src_y;
1207
1208   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1209   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1210 }
1211
1212 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1213 {
1214 #if DEBUG
1215   if (!IN_SCR_FIELD(x, y))
1216   {
1217     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1218     printf("DrawGraphicThruMask(): This should never happen!\n");
1219     return;
1220   }
1221 #endif
1222
1223   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1224                          graphic, frame);
1225
1226   MarkTileDirty(x, y);
1227 }
1228
1229 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1230 {
1231 #if DEBUG
1232   if (!IN_SCR_FIELD(x, y))
1233   {
1234     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1235     printf("DrawGraphicThruMask(): This should never happen!\n");
1236     return;
1237   }
1238 #endif
1239
1240   DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1241                               graphic, frame);
1242   MarkTileDirty(x, y);
1243 }
1244
1245 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1246                             int frame)
1247 {
1248   Bitmap *src_bitmap;
1249   int src_x, src_y;
1250
1251   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1252
1253   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1254                    dst_x, dst_y);
1255 }
1256
1257 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1258                                  int graphic, int frame)
1259 {
1260   struct GraphicInfo *g = &graphic_info[graphic];
1261   Bitmap *src_bitmap;
1262   int src_x, src_y;
1263
1264   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1265
1266   BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1267                    dst_x, dst_y);
1268 }
1269
1270 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1271 {
1272   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1273                       frame, tilesize);
1274   MarkTileDirty(x / tilesize, y / tilesize);
1275 }
1276
1277 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1278                          int tilesize)
1279 {
1280   Bitmap *src_bitmap;
1281   int src_x, src_y;
1282
1283   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1284   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1285 }
1286
1287 void DrawMiniGraphic(int x, int y, int graphic)
1288 {
1289   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1290   MarkTileDirty(x / 2, y / 2);
1291 }
1292
1293 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1294 {
1295   Bitmap *src_bitmap;
1296   int src_x, src_y;
1297
1298   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1299   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1300 }
1301
1302 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1303                                             int graphic, int frame,
1304                                             int cut_mode, int mask_mode)
1305 {
1306   Bitmap *src_bitmap;
1307   int src_x, src_y;
1308   int dst_x, dst_y;
1309   int width = TILEX, height = TILEY;
1310   int cx = 0, cy = 0;
1311
1312   if (dx || dy)                 /* shifted graphic */
1313   {
1314     if (x < BX1)                /* object enters playfield from the left */
1315     {
1316       x = BX1;
1317       width = dx;
1318       cx = TILEX - dx;
1319       dx = 0;
1320     }
1321     else if (x > BX2)           /* object enters playfield from the right */
1322     {
1323       x = BX2;
1324       width = -dx;
1325       dx = TILEX + dx;
1326     }
1327     else if (x==BX1 && dx < 0)  /* object leaves playfield to the left */
1328     {
1329       width += dx;
1330       cx = -dx;
1331       dx = 0;
1332     }
1333     else if (x==BX2 && dx > 0)  /* object leaves playfield to the right */
1334       width -= dx;
1335     else if (dx)                /* general horizontal movement */
1336       MarkTileDirty(x + SIGN(dx), y);
1337
1338     if (y < BY1)                /* object enters playfield from the top */
1339     {
1340       if (cut_mode==CUT_BELOW)  /* object completely above top border */
1341         return;
1342
1343       y = BY1;
1344       height = dy;
1345       cy = TILEY - dy;
1346       dy = 0;
1347     }
1348     else if (y > BY2)           /* object enters playfield from the bottom */
1349     {
1350       y = BY2;
1351       height = -dy;
1352       dy = TILEY + dy;
1353     }
1354     else if (y==BY1 && dy < 0)  /* object leaves playfield to the top */
1355     {
1356       height += dy;
1357       cy = -dy;
1358       dy = 0;
1359     }
1360     else if (dy > 0 && cut_mode == CUT_ABOVE)
1361     {
1362       if (y == BY2)             /* object completely above bottom border */
1363         return;
1364
1365       height = dy;
1366       cy = TILEY - dy;
1367       dy = TILEY;
1368       MarkTileDirty(x, y + 1);
1369     }                           /* object leaves playfield to the bottom */
1370     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1371       height -= dy;
1372     else if (dy)                /* general vertical movement */
1373       MarkTileDirty(x, y + SIGN(dy));
1374   }
1375
1376 #if DEBUG
1377   if (!IN_SCR_FIELD(x, y))
1378   {
1379     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1380     printf("DrawGraphicShifted(): This should never happen!\n");
1381     return;
1382   }
1383 #endif
1384
1385   width = width * TILESIZE_VAR / TILESIZE;
1386   height = height * TILESIZE_VAR / TILESIZE;
1387   cx = cx * TILESIZE_VAR / TILESIZE;
1388   cy = cy * TILESIZE_VAR / TILESIZE;
1389   dx = dx * TILESIZE_VAR / TILESIZE;
1390   dy = dy * TILESIZE_VAR / TILESIZE;
1391
1392   if (width > 0 && height > 0)
1393   {
1394     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1395
1396     src_x += cx;
1397     src_y += cy;
1398
1399     dst_x = FX + x * TILEX_VAR + dx;
1400     dst_y = FY + y * TILEY_VAR + dy;
1401
1402     if (mask_mode == USE_MASKING)
1403       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1404                        dst_x, dst_y);
1405     else
1406       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1407                  dst_x, dst_y);
1408
1409     MarkTileDirty(x, y);
1410   }
1411 }
1412
1413 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1414                                             int graphic, int frame,
1415                                             int cut_mode, int mask_mode)
1416 {
1417   Bitmap *src_bitmap;
1418   int src_x, src_y;
1419   int dst_x, dst_y;
1420   int width = TILEX_VAR, height = TILEY_VAR;
1421   int x1 = x;
1422   int y1 = y;
1423   int x2 = x + SIGN(dx);
1424   int y2 = y + SIGN(dy);
1425
1426   /* movement with two-tile animations must be sync'ed with movement position,
1427      not with current GfxFrame (which can be higher when using slow movement) */
1428   int anim_pos = (dx ? ABS(dx) : ABS(dy));
1429   int anim_frames = graphic_info[graphic].anim_frames;
1430
1431   /* (we also need anim_delay here for movement animations with less frames) */
1432   int anim_delay = graphic_info[graphic].anim_delay;
1433   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1434
1435   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    /* only for falling! */
1436   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    /* only for falling! */
1437
1438   /* re-calculate animation frame for two-tile movement animation */
1439   frame = getGraphicAnimationFrame(graphic, sync_frame);
1440
1441   /* check if movement start graphic inside screen area and should be drawn */
1442   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1443   {
1444     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1445
1446     dst_x = FX + x1 * TILEX_VAR;
1447     dst_y = FY + y1 * TILEY_VAR;
1448
1449     if (mask_mode == USE_MASKING)
1450       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1451                        dst_x, dst_y);
1452     else
1453       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1454                  dst_x, dst_y);
1455
1456     MarkTileDirty(x1, y1);
1457   }
1458
1459   /* check if movement end graphic inside screen area and should be drawn */
1460   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1461   {
1462     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1463
1464     dst_x = FX + x2 * TILEX_VAR;
1465     dst_y = FY + y2 * TILEY_VAR;
1466
1467     if (mask_mode == USE_MASKING)
1468       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1469                        dst_x, dst_y);
1470     else
1471       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1472                  dst_x, dst_y);
1473
1474     MarkTileDirty(x2, y2);
1475   }
1476 }
1477
1478 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1479                                int graphic, int frame,
1480                                int cut_mode, int mask_mode)
1481 {
1482   if (graphic < 0)
1483   {
1484     DrawGraphic(x, y, graphic, frame);
1485
1486     return;
1487   }
1488
1489   if (graphic_info[graphic].double_movement)    /* EM style movement images */
1490     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1491   else
1492     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1493 }
1494
1495 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1496                                 int frame, int cut_mode)
1497 {
1498   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1499 }
1500
1501 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1502                           int cut_mode, int mask_mode)
1503 {
1504   int lx = LEVELX(x), ly = LEVELY(y);
1505   int graphic;
1506   int frame;
1507
1508   if (IN_LEV_FIELD(lx, ly))
1509   {
1510     SetRandomAnimationValue(lx, ly);
1511
1512     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1513     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1514
1515     /* do not use double (EM style) movement graphic when not moving */
1516     if (graphic_info[graphic].double_movement && !dx && !dy)
1517     {
1518       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1519       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1520     }
1521   }
1522   else  /* border element */
1523   {
1524     graphic = el2img(element);
1525     frame = getGraphicAnimationFrame(graphic, -1);
1526   }
1527
1528   if (element == EL_EXPANDABLE_WALL)
1529   {
1530     boolean left_stopped = FALSE, right_stopped = FALSE;
1531
1532     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1533       left_stopped = TRUE;
1534     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1535       right_stopped = TRUE;
1536
1537     if (left_stopped && right_stopped)
1538       graphic = IMG_WALL;
1539     else if (left_stopped)
1540     {
1541       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1542       frame = graphic_info[graphic].anim_frames - 1;
1543     }
1544     else if (right_stopped)
1545     {
1546       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1547       frame = graphic_info[graphic].anim_frames - 1;
1548     }
1549   }
1550
1551   if (dx || dy)
1552     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1553   else if (mask_mode == USE_MASKING)
1554     DrawGraphicThruMask(x, y, graphic, frame);
1555   else
1556     DrawGraphic(x, y, graphic, frame);
1557 }
1558
1559 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1560                          int cut_mode, int mask_mode)
1561 {
1562   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1563     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1564                          cut_mode, mask_mode);
1565 }
1566
1567 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1568                               int cut_mode)
1569 {
1570   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1571 }
1572
1573 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1574                              int cut_mode)
1575 {
1576   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1577 }
1578
1579 void DrawLevelElementThruMask(int x, int y, int element)
1580 {
1581   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1582 }
1583
1584 void DrawLevelFieldThruMask(int x, int y)
1585 {
1586   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1587 }
1588
1589 /* !!! implementation of quicksand is totally broken !!! */
1590 #define IS_CRUMBLED_TILE(x, y, e)                                       \
1591         (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
1592                              !IS_MOVING(x, y) ||                        \
1593                              (e) == EL_QUICKSAND_EMPTYING ||            \
1594                              (e) == EL_QUICKSAND_FAST_EMPTYING))
1595
1596 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1597                                                int graphic)
1598 {
1599   Bitmap *src_bitmap;
1600   int src_x, src_y;
1601   int width, height, cx, cy;
1602   int sx = SCREENX(x), sy = SCREENY(y);
1603   int crumbled_border_size = graphic_info[graphic].border_size;
1604   int i;
1605
1606   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1607
1608   for (i = 1; i < 4; i++)
1609   {
1610     int dxx = (i & 1 ? dx : 0);
1611     int dyy = (i & 2 ? dy : 0);
1612     int xx = x + dxx;
1613     int yy = y + dyy;
1614     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1615                    BorderElement);
1616
1617     /* check if neighbour field is of same crumble type */
1618     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1619                     graphic_info[graphic].class ==
1620                     graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1621
1622     /* return if check prevents inner corner */
1623     if (same == (dxx == dx && dyy == dy))
1624       return;
1625   }
1626
1627   /* if we reach this point, we have an inner corner */
1628
1629   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1630
1631   width  = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1632   height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1633   cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
1634   cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1635
1636   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1637              width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1638 }
1639
1640 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1641                                           int dir)
1642 {
1643   Bitmap *src_bitmap;
1644   int src_x, src_y;
1645   int width, height, bx, by, cx, cy;
1646   int sx = SCREENX(x), sy = SCREENY(y);
1647   int crumbled_border_size = graphic_info[graphic].border_size;
1648   int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1649   int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1650   int i;
1651
1652   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1653
1654   /* draw simple, sloppy, non-corner-accurate crumbled border */
1655
1656   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1657   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1658   cx = (dir == 2 ? crumbled_border_pos_var : 0);
1659   cy = (dir == 3 ? crumbled_border_pos_var : 0);
1660
1661   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1662              FX + sx * TILEX_VAR + cx,
1663              FY + sy * TILEY_VAR + cy);
1664
1665   /* (remaining middle border part must be at least as big as corner part) */
1666   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1667       crumbled_border_size >= TILESIZE / 3)
1668     return;
1669
1670   /* correct corners of crumbled border, if needed */
1671
1672   for (i = -1; i <= 1; i += 2)
1673   {
1674     int xx = x + (dir == 0 || dir == 3 ? i : 0);
1675     int yy = y + (dir == 1 || dir == 2 ? i : 0);
1676     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1677                    BorderElement);
1678
1679     /* check if neighbour field is of same crumble type */
1680     if (IS_CRUMBLED_TILE(xx, yy, element) &&
1681         graphic_info[graphic].class ==
1682         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1683     {
1684       /* no crumbled corner, but continued crumbled border */
1685
1686       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1687       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1688       int b1 = (i == 1 ? crumbled_border_size_var :
1689                 TILESIZE_VAR - 2 * crumbled_border_size_var);
1690
1691       width  = crumbled_border_size_var;
1692       height = crumbled_border_size_var;
1693
1694       if (dir == 1 || dir == 2)
1695       {
1696         cx = c1;
1697         cy = c2;
1698         bx = cx;
1699         by = b1;
1700       }
1701       else
1702       {
1703         cx = c2;
1704         cy = c1;
1705         bx = b1;
1706         by = cy;
1707       }
1708
1709       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1710                  width, height,
1711                  FX + sx * TILEX_VAR + cx,
1712                  FY + sy * TILEY_VAR + cy);
1713     }
1714   }
1715 }
1716
1717 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1718 {
1719   int sx = SCREENX(x), sy = SCREENY(y);
1720   int element;
1721   int i;
1722   static int xy[4][2] =
1723   {
1724     { 0, -1 },
1725     { -1, 0 },
1726     { +1, 0 },
1727     { 0, +1 }
1728   };
1729
1730   if (!IN_LEV_FIELD(x, y))
1731     return;
1732
1733   element = TILE_GFX_ELEMENT(x, y);
1734
1735   /* crumble field itself */
1736   if (IS_CRUMBLED_TILE(x, y, element))
1737   {
1738     if (!IN_SCR_FIELD(sx, sy))
1739       return;
1740
1741     for (i = 0; i < 4; i++)
1742     {
1743       int xx = x + xy[i][0];
1744       int yy = y + xy[i][1];
1745
1746       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1747                  BorderElement);
1748
1749       /* check if neighbour field is of same crumble type */
1750       if (IS_CRUMBLED_TILE(xx, yy, element) &&
1751           graphic_info[graphic].class ==
1752           graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1753         continue;
1754
1755       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1756     }
1757
1758     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1759         graphic_info[graphic].anim_frames == 2)
1760     {
1761       for (i = 0; i < 4; i++)
1762       {
1763         int dx = (i & 1 ? +1 : -1);
1764         int dy = (i & 2 ? +1 : -1);
1765
1766         DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1767       }
1768     }
1769
1770     MarkTileDirty(sx, sy);
1771   }
1772   else          /* center field not crumbled -- crumble neighbour fields */
1773   {
1774     for (i = 0; i < 4; i++)
1775     {
1776       int xx = x + xy[i][0];
1777       int yy = y + xy[i][1];
1778       int sxx = sx + xy[i][0];
1779       int syy = sy + xy[i][1];
1780
1781       if (!IN_LEV_FIELD(xx, yy) ||
1782           !IN_SCR_FIELD(sxx, syy))
1783         continue;
1784
1785       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1786         continue;
1787
1788       element = TILE_GFX_ELEMENT(xx, yy);
1789
1790       if (!IS_CRUMBLED_TILE(xx, yy, element))
1791         continue;
1792
1793       graphic = el_act2crm(element, ACTION_DEFAULT);
1794
1795       DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1796
1797       MarkTileDirty(sxx, syy);
1798     }
1799   }
1800 }
1801
1802 void DrawLevelFieldCrumbled(int x, int y)
1803 {
1804   int graphic;
1805
1806   if (!IN_LEV_FIELD(x, y))
1807     return;
1808
1809   if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1810       GfxElement[x][y] != EL_UNDEFINED &&
1811       GFX_CRUMBLED(GfxElement[x][y]))
1812   {
1813     DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1814
1815     return;
1816   }
1817
1818   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1819
1820   DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1821 }
1822
1823 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1824                                    int step_frame)
1825 {
1826   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1827   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1828   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1829   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1830   int sx = SCREENX(x), sy = SCREENY(y);
1831
1832   DrawGraphic(sx, sy, graphic1, frame1);
1833   DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1834 }
1835
1836 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1837 {
1838   int sx = SCREENX(x), sy = SCREENY(y);
1839   static int xy[4][2] =
1840   {
1841     { 0, -1 },
1842     { -1, 0 },
1843     { +1, 0 },
1844     { 0, +1 }
1845   };
1846   int i;
1847
1848   for (i = 0; i < 4; i++)
1849   {
1850     int xx = x + xy[i][0];
1851     int yy = y + xy[i][1];
1852     int sxx = sx + xy[i][0];
1853     int syy = sy + xy[i][1];
1854
1855     if (!IN_LEV_FIELD(xx, yy) ||
1856         !IN_SCR_FIELD(sxx, syy) ||
1857         !GFX_CRUMBLED(Feld[xx][yy]) ||
1858         IS_MOVING(xx, yy))
1859       continue;
1860
1861     DrawLevelField(xx, yy);
1862   }
1863 }
1864
1865 static int getBorderElement(int x, int y)
1866 {
1867   int border[7][2] =
1868   {
1869     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
1870     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
1871     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
1872     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1873     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
1874     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
1875     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
1876   };
1877   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1878   int steel_position = (x == -1         && y == -1              ? 0 :
1879                         x == lev_fieldx && y == -1              ? 1 :
1880                         x == -1         && y == lev_fieldy      ? 2 :
1881                         x == lev_fieldx && y == lev_fieldy      ? 3 :
1882                         x == -1         || x == lev_fieldx      ? 4 :
1883                         y == -1         || y == lev_fieldy      ? 5 : 6);
1884
1885   return border[steel_position][steel_type];
1886 }
1887
1888 void DrawScreenElement(int x, int y, int element)
1889 {
1890   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1891   DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1892 }
1893
1894 void DrawLevelElement(int x, int y, int element)
1895 {
1896   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1897     DrawScreenElement(SCREENX(x), SCREENY(y), element);
1898 }
1899
1900 void DrawScreenField(int x, int y)
1901 {
1902   int lx = LEVELX(x), ly = LEVELY(y);
1903   int element, content;
1904
1905   if (!IN_LEV_FIELD(lx, ly))
1906   {
1907     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1908       element = EL_EMPTY;
1909     else
1910       element = getBorderElement(lx, ly);
1911
1912     DrawScreenElement(x, y, element);
1913
1914     return;
1915   }
1916
1917   element = Feld[lx][ly];
1918   content = Store[lx][ly];
1919
1920   if (IS_MOVING(lx, ly))
1921   {
1922     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1923     boolean cut_mode = NO_CUTTING;
1924
1925     if (element == EL_QUICKSAND_EMPTYING ||
1926         element == EL_QUICKSAND_FAST_EMPTYING ||
1927         element == EL_MAGIC_WALL_EMPTYING ||
1928         element == EL_BD_MAGIC_WALL_EMPTYING ||
1929         element == EL_DC_MAGIC_WALL_EMPTYING ||
1930         element == EL_AMOEBA_DROPPING)
1931       cut_mode = CUT_ABOVE;
1932     else if (element == EL_QUICKSAND_FILLING ||
1933              element == EL_QUICKSAND_FAST_FILLING ||
1934              element == EL_MAGIC_WALL_FILLING ||
1935              element == EL_BD_MAGIC_WALL_FILLING ||
1936              element == EL_DC_MAGIC_WALL_FILLING)
1937       cut_mode = CUT_BELOW;
1938
1939     if (cut_mode == CUT_ABOVE)
1940       DrawScreenElement(x, y, element);
1941     else
1942       DrawScreenElement(x, y, EL_EMPTY);
1943
1944     if (horiz_move)
1945       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1946     else if (cut_mode == NO_CUTTING)
1947       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1948     else
1949     {
1950       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1951
1952       if (cut_mode == CUT_BELOW &&
1953           IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1954         DrawLevelElement(lx, ly + 1, element);
1955     }
1956
1957     if (content == EL_ACID)
1958     {
1959       int dir = MovDir[lx][ly];
1960       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1961       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
1962
1963       DrawLevelElementThruMask(newlx, newly, EL_ACID);
1964     }
1965   }
1966   else if (IS_BLOCKED(lx, ly))
1967   {
1968     int oldx, oldy;
1969     int sx, sy;
1970     int horiz_move;
1971     boolean cut_mode = NO_CUTTING;
1972     int element_old, content_old;
1973
1974     Blocked2Moving(lx, ly, &oldx, &oldy);
1975     sx = SCREENX(oldx);
1976     sy = SCREENY(oldy);
1977     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1978                   MovDir[oldx][oldy] == MV_RIGHT);
1979
1980     element_old = Feld[oldx][oldy];
1981     content_old = Store[oldx][oldy];
1982
1983     if (element_old == EL_QUICKSAND_EMPTYING ||
1984         element_old == EL_QUICKSAND_FAST_EMPTYING ||
1985         element_old == EL_MAGIC_WALL_EMPTYING ||
1986         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1987         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1988         element_old == EL_AMOEBA_DROPPING)
1989       cut_mode = CUT_ABOVE;
1990
1991     DrawScreenElement(x, y, EL_EMPTY);
1992
1993     if (horiz_move)
1994       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1995                                NO_CUTTING);
1996     else if (cut_mode == NO_CUTTING)
1997       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1998                                cut_mode);
1999     else
2000       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2001                                cut_mode);
2002   }
2003   else if (IS_DRAWABLE(element))
2004     DrawScreenElement(x, y, element);
2005   else
2006     DrawScreenElement(x, y, EL_EMPTY);
2007 }
2008
2009 void DrawLevelField(int x, int y)
2010 {
2011   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2012     DrawScreenField(SCREENX(x), SCREENY(y));
2013   else if (IS_MOVING(x, y))
2014   {
2015     int newx,newy;
2016
2017     Moving2Blocked(x, y, &newx, &newy);
2018     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2019       DrawScreenField(SCREENX(newx), SCREENY(newy));
2020   }
2021   else if (IS_BLOCKED(x, y))
2022   {
2023     int oldx, oldy;
2024
2025     Blocked2Moving(x, y, &oldx, &oldy);
2026     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2027       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2028   }
2029 }
2030
2031 void DrawSizedElement(int x, int y, int element, int tilesize)
2032 {
2033   int graphic;
2034
2035   graphic = el2edimg(element);
2036   DrawSizedGraphic(x, y, graphic, 0, tilesize);
2037 }
2038
2039 void DrawMiniElement(int x, int y, int element)
2040 {
2041   int graphic;
2042
2043   graphic = el2edimg(element);
2044   DrawMiniGraphic(x, y, graphic);
2045 }
2046
2047 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2048                             int tilesize)
2049 {
2050   int x = sx + scroll_x, y = sy + scroll_y;
2051
2052   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2053     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2054   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2055     DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2056   else
2057     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2058 }
2059
2060 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2061 {
2062   int x = sx + scroll_x, y = sy + scroll_y;
2063
2064   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2065     DrawMiniElement(sx, sy, EL_EMPTY);
2066   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2067     DrawMiniElement(sx, sy, Feld[x][y]);
2068   else
2069     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2070 }
2071
2072 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2073                                  int x, int y, int xsize, int ysize,
2074                                  int tile_width, int tile_height)
2075 {
2076   Bitmap *src_bitmap;
2077   int src_x, src_y;
2078   int dst_x = startx + x * tile_width;
2079   int dst_y = starty + y * tile_height;
2080   int width  = graphic_info[graphic].width;
2081   int height = graphic_info[graphic].height;
2082   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2083   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2084   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2085   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2086   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2087   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2088   boolean draw_masked = graphic_info[graphic].draw_masked;
2089
2090   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2091
2092   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2093   {
2094     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2095     return;
2096   }
2097
2098   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2099             inner_sx + (x - 1) * tile_width  % inner_width);
2100   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2101             inner_sy + (y - 1) * tile_height % inner_height);
2102
2103   if (draw_masked)
2104     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2105                      dst_x, dst_y);
2106   else
2107     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2108                dst_x, dst_y);
2109 }
2110
2111 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2112                             int x, int y, int xsize, int ysize, int font_nr)
2113 {
2114   int font_width  = getFontWidth(font_nr);
2115   int font_height = getFontHeight(font_nr);
2116
2117   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2118                               font_width, font_height);
2119 }
2120
2121 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2122 {
2123   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2124   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2125   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2126   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2127   boolean no_delay = (tape.warp_forward);
2128   unsigned int anim_delay = 0;
2129   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2130   int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2131   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2132   int font_width = getFontWidth(font_nr);
2133   int font_height = getFontHeight(font_nr);
2134   int max_xsize = level.envelope[envelope_nr].xsize;
2135   int max_ysize = level.envelope[envelope_nr].ysize;
2136   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2137   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2138   int xend = max_xsize;
2139   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2140   int xstep = (xstart < xend ? 1 : 0);
2141   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2142   int start = 0;
2143   int end = MAX(xend - xstart, yend - ystart);
2144   int i;
2145
2146   for (i = start; i <= end; i++)
2147   {
2148     int last_frame = end;       // last frame of this "for" loop
2149     int x = xstart + i * xstep;
2150     int y = ystart + i * ystep;
2151     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2152     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2153     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2154     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2155     int xx, yy;
2156
2157     SetDrawtoField(DRAW_FIELDBUFFER);
2158
2159     BlitScreenToBitmap(backbuffer);
2160
2161     SetDrawtoField(DRAW_BACKBUFFER);
2162
2163     for (yy = 0; yy < ysize; yy++)
2164       for (xx = 0; xx < xsize; xx++)
2165         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2166
2167     DrawTextBuffer(sx + font_width, sy + font_height,
2168                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2169                    xsize - 2, ysize - 2, 0, mask_mode,
2170                    level.envelope[envelope_nr].autowrap,
2171                    level.envelope[envelope_nr].centered, FALSE);
2172
2173     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2174     BackToFront();
2175
2176     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2177   }
2178 }
2179
2180 void ShowEnvelope(int envelope_nr)
2181 {
2182   int element = EL_ENVELOPE_1 + envelope_nr;
2183   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2184   int sound_opening = element_info[element].sound[ACTION_OPENING];
2185   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2186   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2187   boolean no_delay = (tape.warp_forward);
2188   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2189   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2190   int anim_mode = graphic_info[graphic].anim_mode;
2191   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2192                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2193
2194   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2195
2196   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2197
2198   if (anim_mode == ANIM_DEFAULT)
2199     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2200
2201   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2202
2203   if (tape.playing)
2204     Delay(wait_delay_value);
2205   else
2206     WaitForEventToContinue();
2207
2208   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2209
2210   if (anim_mode != ANIM_NONE)
2211     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2212
2213   if (anim_mode == ANIM_DEFAULT)
2214     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2215
2216   game.envelope_active = FALSE;
2217
2218   SetDrawtoField(DRAW_FIELDBUFFER);
2219
2220   redraw_mask |= REDRAW_FIELD;
2221   BackToFront();
2222 }
2223
2224 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2225 {
2226   int border_size = request.border_size;
2227   int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2228   int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2229   int sx = sx_center - request.width  / 2;
2230   int sy = sy_center - request.height / 2;
2231
2232   if (add_border_size)
2233   {
2234     sx += border_size;
2235     sy += border_size;
2236   }
2237
2238   *x = sx;
2239   *y = sy;
2240 }
2241
2242 void DrawEnvelopeRequest(char *text)
2243 {
2244   char *text_final = text;
2245   char *text_door_style = NULL;
2246   int graphic = IMG_BACKGROUND_REQUEST;
2247   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2248   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2249   int font_nr = FONT_REQUEST;
2250   int font_width = getFontWidth(font_nr);
2251   int font_height = getFontHeight(font_nr);
2252   int border_size = request.border_size;
2253   int line_spacing = request.line_spacing;
2254   int line_height = font_height + line_spacing;
2255   int text_width = request.width - 2 * border_size;
2256   int text_height = request.height - 2 * border_size;
2257   int line_length = text_width / font_width;
2258   int max_lines = text_height / line_height;
2259   int width = request.width;
2260   int height = request.height;
2261   int tile_size = request.step_offset;
2262   int x_steps = width  / tile_size;
2263   int y_steps = height / tile_size;
2264   int sx, sy;
2265   int i, x, y;
2266
2267   if (request.wrap_single_words)
2268   {
2269     char *src_text_ptr, *dst_text_ptr;
2270
2271     text_door_style = checked_malloc(2 * strlen(text) + 1);
2272
2273     src_text_ptr = text;
2274     dst_text_ptr = text_door_style;
2275
2276     while (*src_text_ptr)
2277     {
2278       if (*src_text_ptr == ' ' ||
2279           *src_text_ptr == '?' ||
2280           *src_text_ptr == '!')
2281         *dst_text_ptr++ = '\n';
2282
2283       if (*src_text_ptr != ' ')
2284         *dst_text_ptr++ = *src_text_ptr;
2285
2286       src_text_ptr++;
2287     }
2288
2289     *dst_text_ptr = '\0';
2290
2291     text_final = text_door_style;
2292   }
2293
2294   setRequestPosition(&sx, &sy, FALSE);
2295
2296   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2297
2298   for (y = 0; y < y_steps; y++)
2299     for (x = 0; x < x_steps; x++)
2300       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2301                                   x, y, x_steps, y_steps,
2302                                   tile_size, tile_size);
2303
2304   DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2305                  line_length, -1, max_lines, line_spacing, mask_mode,
2306                  request.autowrap, request.centered, FALSE);
2307
2308   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2309     RedrawGadget(tool_gadget[i]);
2310
2311   // store readily prepared envelope request for later use when animating
2312   BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2313
2314   if (text_door_style)
2315     free(text_door_style);
2316 }
2317
2318 void AnimateEnvelopeRequest(int anim_mode, int action)
2319 {
2320   int graphic = IMG_BACKGROUND_REQUEST;
2321   boolean draw_masked = graphic_info[graphic].draw_masked;
2322   int delay_value_normal = request.step_delay;
2323   int delay_value_fast = delay_value_normal / 2;
2324   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2325   boolean no_delay = (tape.warp_forward);
2326   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2327   int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2328   unsigned int anim_delay = 0;
2329
2330   int width = request.width;
2331   int height = request.height;
2332   int tile_size = request.step_offset;
2333   int max_xsize = width  / tile_size;
2334   int max_ysize = height / tile_size;
2335   int max_xsize_inner = max_xsize - 2;
2336   int max_ysize_inner = max_ysize - 2;
2337
2338   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2339   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2340   int xend = max_xsize_inner;
2341   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2342   int xstep = (xstart < xend ? 1 : 0);
2343   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2344   int start = 0;
2345   int end = MAX(xend - xstart, yend - ystart);
2346   int i;
2347
2348   if (setup.quick_doors)
2349   {
2350     xstart = xend;
2351     ystart = yend;
2352     end = 0;
2353   }
2354   else
2355   {
2356     if (action == ACTION_OPENING)
2357       PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2358     else if (action == ACTION_CLOSING)
2359       PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2360   }
2361
2362   for (i = start; i <= end; i++)
2363   {
2364     int last_frame = end;       // last frame of this "for" loop
2365     int x = xstart + i * xstep;
2366     int y = ystart + i * ystep;
2367     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2368     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2369     int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2370     int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2371     int src_x = sx_center - width  / 2;
2372     int src_y = sy_center - height / 2;
2373     int dst_x = sx_center - xsize * tile_size / 2;
2374     int dst_y = sy_center - ysize * tile_size / 2;
2375     int xsize_size_left = (xsize - 1) * tile_size;
2376     int ysize_size_top  = (ysize - 1) * tile_size;
2377     int max_xsize_pos = (max_xsize - 1) * tile_size;
2378     int max_ysize_pos = (max_ysize - 1) * tile_size;
2379     int xx, yy;
2380
2381     BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2382
2383     for (yy = 0; yy < 2; yy++)
2384     {
2385       for (xx = 0; xx < 2; xx++)
2386       {
2387         int src_xx = src_x + xx * max_xsize_pos;
2388         int src_yy = src_y + yy * max_ysize_pos;
2389         int dst_xx = dst_x + xx * xsize_size_left;
2390         int dst_yy = dst_y + yy * ysize_size_top;
2391         int xx_size = (xx ? tile_size : xsize_size_left);
2392         int yy_size = (yy ? tile_size : ysize_size_top);
2393
2394         if (draw_masked)
2395           BlitBitmapMasked(bitmap_db_cross, backbuffer,
2396                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2397         else
2398           BlitBitmap(bitmap_db_cross, backbuffer,
2399                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2400       }
2401     }
2402
2403     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2404
2405     DoAnimation();
2406     BackToFront();
2407
2408     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2409   }
2410 }
2411
2412
2413 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2414 {
2415   int last_game_status = game_status;   /* save current game status */
2416   int graphic = IMG_BACKGROUND_REQUEST;
2417   int sound_opening = SND_REQUEST_OPENING;
2418   int sound_closing = SND_REQUEST_CLOSING;
2419   int anim_mode = graphic_info[graphic].anim_mode;
2420   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2421                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2422
2423   if (game_status == GAME_MODE_PLAYING)
2424     BlitScreenToBitmap(backbuffer);
2425
2426   SetDrawtoField(DRAW_BACKBUFFER);
2427
2428   // SetDrawBackgroundMask(REDRAW_NONE);
2429
2430   if (action == ACTION_OPENING)
2431   {
2432     BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2433
2434     if (req_state & REQ_ASK)
2435     {
2436       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2437       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2438     }
2439     else if (req_state & REQ_CONFIRM)
2440     {
2441       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2442     }
2443     else if (req_state & REQ_PLAYER)
2444     {
2445       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2446       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2447       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2448       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2449     }
2450
2451     DrawEnvelopeRequest(text);
2452
2453     if (game_status != GAME_MODE_MAIN)
2454       InitAnimation();
2455   }
2456
2457   /* force DOOR font inside door area */
2458   game_status = GAME_MODE_PSEUDO_DOOR;
2459
2460   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2461
2462   if (action == ACTION_OPENING)
2463   {
2464     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2465
2466     if (anim_mode == ANIM_DEFAULT)
2467       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2468
2469     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2470   }
2471   else
2472   {
2473     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2474
2475     if (anim_mode != ANIM_NONE)
2476       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2477
2478     if (anim_mode == ANIM_DEFAULT)
2479       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2480   }
2481
2482   game.envelope_active = FALSE;
2483
2484   game_status = last_game_status;       /* restore current game status */
2485
2486   if (action == ACTION_CLOSING)
2487   {
2488     if (game_status != GAME_MODE_MAIN)
2489       StopAnimation();
2490
2491     BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2492   }
2493
2494   // SetDrawBackgroundMask(last_draw_background_mask);
2495
2496   redraw_mask |= REDRAW_FIELD;
2497
2498   if (game_status == GAME_MODE_MAIN)
2499     DoAnimation();
2500
2501   BackToFront();
2502
2503   if (action == ACTION_CLOSING &&
2504       game_status == GAME_MODE_PLAYING &&
2505       level.game_engine_type == GAME_ENGINE_TYPE_RND)
2506     SetDrawtoField(DRAW_FIELDBUFFER);
2507 }
2508
2509 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2510 {
2511   Bitmap *src_bitmap;
2512   int src_x, src_y;
2513   int graphic = el2preimg(element);
2514
2515   getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2516   BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2517 }
2518
2519 void DrawLevel(int draw_background_mask)
2520 {
2521   int x,y;
2522
2523   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2524   SetDrawBackgroundMask(draw_background_mask);
2525
2526   ClearField();
2527
2528   for (x = BX1; x <= BX2; x++)
2529     for (y = BY1; y <= BY2; y++)
2530       DrawScreenField(x, y);
2531
2532   redraw_mask |= REDRAW_FIELD;
2533 }
2534
2535 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2536                     int tilesize)
2537 {
2538   int x,y;
2539
2540   for (x = 0; x < size_x; x++)
2541     for (y = 0; y < size_y; y++)
2542       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2543
2544   redraw_mask |= REDRAW_FIELD;
2545 }
2546
2547 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2548 {
2549   int x,y;
2550
2551   for (x = 0; x < size_x; x++)
2552     for (y = 0; y < size_y; y++)
2553       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2554
2555   redraw_mask |= REDRAW_FIELD;
2556 }
2557
2558 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2559 {
2560   boolean show_level_border = (BorderElement != EL_EMPTY);
2561   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2562   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2563   int tile_size = preview.tile_size;
2564   int preview_width  = preview.xsize * tile_size;
2565   int preview_height = preview.ysize * tile_size;
2566   int real_preview_xsize = MIN(level_xsize, preview.xsize);
2567   int real_preview_ysize = MIN(level_ysize, preview.ysize);
2568   int real_preview_width  = real_preview_xsize * tile_size;
2569   int real_preview_height = real_preview_ysize * tile_size;
2570   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2571   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2572   int x, y;
2573
2574   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2575     return;
2576
2577   DrawBackground(dst_x, dst_y, preview_width, preview_height);
2578
2579   dst_x += (preview_width  - real_preview_width)  / 2;
2580   dst_y += (preview_height - real_preview_height) / 2;
2581
2582   for (x = 0; x < real_preview_xsize; x++)
2583   {
2584     for (y = 0; y < real_preview_ysize; y++)
2585     {
2586       int lx = from_x + x + (show_level_border ? -1 : 0);
2587       int ly = from_y + y + (show_level_border ? -1 : 0);
2588       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2589                      getBorderElement(lx, ly));
2590
2591       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2592                          element, tile_size);
2593     }
2594   }
2595
2596   redraw_mask |= REDRAW_MICROLEVEL;
2597 }
2598
2599 #define MICROLABEL_EMPTY                0
2600 #define MICROLABEL_LEVEL_NAME           1
2601 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
2602 #define MICROLABEL_LEVEL_AUTHOR         3
2603 #define MICROLABEL_IMPORTED_FROM_HEAD   4
2604 #define MICROLABEL_IMPORTED_FROM        5
2605 #define MICROLABEL_IMPORTED_BY_HEAD     6
2606 #define MICROLABEL_IMPORTED_BY          7
2607
2608 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2609 {
2610   int max_text_width = SXSIZE;
2611   int font_width = getFontWidth(font_nr);
2612
2613   if (pos->align == ALIGN_CENTER)
2614     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2615   else if (pos->align == ALIGN_RIGHT)
2616     max_text_width = pos->x;
2617   else
2618     max_text_width = SXSIZE - pos->x;
2619
2620   return max_text_width / font_width;
2621 }
2622
2623 static void DrawPreviewLevelLabelExt(int mode)
2624 {
2625   struct TextPosInfo *pos = &menu.main.text.level_info_2;
2626   char label_text[MAX_OUTPUT_LINESIZE + 1];
2627   int max_len_label_text;
2628   int font_nr = pos->font;
2629   int i;
2630
2631   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2632     return;
2633
2634   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2635       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2636       mode == MICROLABEL_IMPORTED_BY_HEAD)
2637     font_nr = pos->font_alt;
2638
2639   max_len_label_text = getMaxTextLength(pos, font_nr);
2640
2641   if (pos->size != -1)
2642     max_len_label_text = pos->size;
2643
2644   for (i = 0; i < max_len_label_text; i++)
2645     label_text[i] = ' ';
2646   label_text[max_len_label_text] = '\0';
2647
2648   if (strlen(label_text) > 0)
2649     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2650
2651   strncpy(label_text,
2652           (mode == MICROLABEL_LEVEL_NAME ? level.name :
2653            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2654            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2655            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2656            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2657            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2658            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2659           max_len_label_text);
2660   label_text[max_len_label_text] = '\0';
2661
2662   if (strlen(label_text) > 0)
2663     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2664
2665   redraw_mask |= REDRAW_MICROLEVEL;
2666 }
2667
2668 static void DrawPreviewLevelExt(boolean restart)
2669 {
2670   static unsigned int scroll_delay = 0;
2671   static unsigned int label_delay = 0;
2672   static int from_x, from_y, scroll_direction;
2673   static int label_state, label_counter;
2674   unsigned int scroll_delay_value = preview.step_delay;
2675   boolean show_level_border = (BorderElement != EL_EMPTY);
2676   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2677   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2678   int last_game_status = game_status;           /* save current game status */
2679
2680   if (restart)
2681   {
2682     from_x = 0;
2683     from_y = 0;
2684
2685     if (preview.anim_mode == ANIM_CENTERED)
2686     {
2687       if (level_xsize > preview.xsize)
2688         from_x = (level_xsize - preview.xsize) / 2;
2689       if (level_ysize > preview.ysize)
2690         from_y = (level_ysize - preview.ysize) / 2;
2691     }
2692
2693     from_x += preview.xoffset;
2694     from_y += preview.yoffset;
2695
2696     scroll_direction = MV_RIGHT;
2697     label_state = 1;
2698     label_counter = 0;
2699
2700     DrawPreviewLevelPlayfieldExt(from_x, from_y);
2701     DrawPreviewLevelLabelExt(label_state);
2702
2703     /* initialize delay counters */
2704     DelayReached(&scroll_delay, 0);
2705     DelayReached(&label_delay, 0);
2706
2707     if (leveldir_current->name)
2708     {
2709       struct TextPosInfo *pos = &menu.main.text.level_info_1;
2710       char label_text[MAX_OUTPUT_LINESIZE + 1];
2711       int font_nr = pos->font;
2712       int max_len_label_text = getMaxTextLength(pos, font_nr);
2713
2714       if (pos->size != -1)
2715         max_len_label_text = pos->size;
2716
2717       strncpy(label_text, leveldir_current->name, max_len_label_text);
2718       label_text[max_len_label_text] = '\0';
2719
2720       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2721         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2722     }
2723
2724     game_status = last_game_status;     /* restore current game status */
2725
2726     return;
2727   }
2728
2729   /* scroll preview level, if needed */
2730   if (preview.anim_mode != ANIM_NONE &&
2731       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2732       DelayReached(&scroll_delay, scroll_delay_value))
2733   {
2734     switch (scroll_direction)
2735     {
2736       case MV_LEFT:
2737         if (from_x > 0)
2738         {
2739           from_x -= preview.step_offset;
2740           from_x = (from_x < 0 ? 0 : from_x);
2741         }
2742         else
2743           scroll_direction = MV_UP;
2744         break;
2745
2746       case MV_RIGHT:
2747         if (from_x < level_xsize - preview.xsize)
2748         {
2749           from_x += preview.step_offset;
2750           from_x = (from_x > level_xsize - preview.xsize ?
2751                     level_xsize - preview.xsize : from_x);
2752         }
2753         else
2754           scroll_direction = MV_DOWN;
2755         break;
2756
2757       case MV_UP:
2758         if (from_y > 0)
2759         {
2760           from_y -= preview.step_offset;
2761           from_y = (from_y < 0 ? 0 : from_y);
2762         }
2763         else
2764           scroll_direction = MV_RIGHT;
2765         break;
2766
2767       case MV_DOWN:
2768         if (from_y < level_ysize - preview.ysize)
2769         {
2770           from_y += preview.step_offset;
2771           from_y = (from_y > level_ysize - preview.ysize ?
2772                     level_ysize - preview.ysize : from_y);
2773         }
2774         else
2775           scroll_direction = MV_LEFT;
2776         break;
2777
2778       default:
2779         break;
2780     }
2781
2782     DrawPreviewLevelPlayfieldExt(from_x, from_y);
2783   }
2784
2785   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2786   /* redraw micro level label, if needed */
2787   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2788       !strEqual(level.author, ANONYMOUS_NAME) &&
2789       !strEqual(level.author, leveldir_current->name) &&
2790       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2791   {
2792     int max_label_counter = 23;
2793
2794     if (leveldir_current->imported_from != NULL &&
2795         strlen(leveldir_current->imported_from) > 0)
2796       max_label_counter += 14;
2797     if (leveldir_current->imported_by != NULL &&
2798         strlen(leveldir_current->imported_by) > 0)
2799       max_label_counter += 14;
2800
2801     label_counter = (label_counter + 1) % max_label_counter;
2802     label_state = (label_counter >= 0 && label_counter <= 7 ?
2803                    MICROLABEL_LEVEL_NAME :
2804                    label_counter >= 9 && label_counter <= 12 ?
2805                    MICROLABEL_LEVEL_AUTHOR_HEAD :
2806                    label_counter >= 14 && label_counter <= 21 ?
2807                    MICROLABEL_LEVEL_AUTHOR :
2808                    label_counter >= 23 && label_counter <= 26 ?
2809                    MICROLABEL_IMPORTED_FROM_HEAD :
2810                    label_counter >= 28 && label_counter <= 35 ?
2811                    MICROLABEL_IMPORTED_FROM :
2812                    label_counter >= 37 && label_counter <= 40 ?
2813                    MICROLABEL_IMPORTED_BY_HEAD :
2814                    label_counter >= 42 && label_counter <= 49 ?
2815                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2816
2817     if (leveldir_current->imported_from == NULL &&
2818         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2819          label_state == MICROLABEL_IMPORTED_FROM))
2820       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2821                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2822
2823     DrawPreviewLevelLabelExt(label_state);
2824   }
2825
2826   game_status = last_game_status;       /* restore current game status */
2827 }
2828
2829 void DrawPreviewLevelInitial()
2830 {
2831   DrawPreviewLevelExt(TRUE);
2832 }
2833
2834 void DrawPreviewLevelAnimation()
2835 {
2836   DrawPreviewLevelExt(FALSE);
2837 }
2838
2839 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2840                                     int graphic, int sync_frame, int mask_mode)
2841 {
2842   int frame = getGraphicAnimationFrame(graphic, sync_frame);
2843
2844   if (mask_mode == USE_MASKING)
2845     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2846   else
2847     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2848 }
2849
2850 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2851                                          int graphic, int sync_frame,
2852                                          int mask_mode)
2853 {
2854   int frame = getGraphicAnimationFrame(graphic, sync_frame);
2855
2856   if (mask_mode == USE_MASKING)
2857     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2858   else
2859     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2860 }
2861
2862 inline void DrawGraphicAnimation(int x, int y, int graphic)
2863 {
2864   int lx = LEVELX(x), ly = LEVELY(y);
2865
2866   if (!IN_SCR_FIELD(x, y))
2867     return;
2868
2869   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2870                           graphic, GfxFrame[lx][ly], NO_MASKING);
2871
2872   MarkTileDirty(x, y);
2873 }
2874
2875 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2876 {
2877   int lx = LEVELX(x), ly = LEVELY(y);
2878
2879   if (!IN_SCR_FIELD(x, y))
2880     return;
2881
2882   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2883                           graphic, GfxFrame[lx][ly], NO_MASKING);
2884   MarkTileDirty(x, y);
2885 }
2886
2887 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2888 {
2889   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2890 }
2891
2892 void DrawLevelElementAnimation(int x, int y, int element)
2893 {
2894   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2895
2896   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2897 }
2898
2899 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2900 {
2901   int sx = SCREENX(x), sy = SCREENY(y);
2902
2903   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2904     return;
2905
2906   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2907     return;
2908
2909   DrawGraphicAnimation(sx, sy, graphic);
2910
2911 #if 1
2912   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2913     DrawLevelFieldCrumbled(x, y);
2914 #else
2915   if (GFX_CRUMBLED(Feld[x][y]))
2916     DrawLevelFieldCrumbled(x, y);
2917 #endif
2918 }
2919
2920 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2921 {
2922   int sx = SCREENX(x), sy = SCREENY(y);
2923   int graphic;
2924
2925   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2926     return;
2927
2928   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2929
2930   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2931     return;
2932
2933   DrawGraphicAnimation(sx, sy, graphic);
2934
2935   if (GFX_CRUMBLED(element))
2936     DrawLevelFieldCrumbled(x, y);
2937 }
2938
2939 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2940 {
2941   if (player->use_murphy)
2942   {
2943     /* this works only because currently only one player can be "murphy" ... */
2944     static int last_horizontal_dir = MV_LEFT;
2945     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2946
2947     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2948       last_horizontal_dir = move_dir;
2949
2950     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
2951     {
2952       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2953
2954       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2955     }
2956
2957     return graphic;
2958   }
2959   else
2960     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2961 }
2962
2963 static boolean equalGraphics(int graphic1, int graphic2)
2964 {
2965   struct GraphicInfo *g1 = &graphic_info[graphic1];
2966   struct GraphicInfo *g2 = &graphic_info[graphic2];
2967
2968   return (g1->bitmap      == g2->bitmap &&
2969           g1->src_x       == g2->src_x &&
2970           g1->src_y       == g2->src_y &&
2971           g1->anim_frames == g2->anim_frames &&
2972           g1->anim_delay  == g2->anim_delay &&
2973           g1->anim_mode   == g2->anim_mode);
2974 }
2975
2976 void DrawAllPlayers()
2977 {
2978   int i;
2979
2980   for (i = 0; i < MAX_PLAYERS; i++)
2981     if (stored_player[i].active)
2982       DrawPlayer(&stored_player[i]);
2983 }
2984
2985 void DrawPlayerField(int x, int y)
2986 {
2987   if (!IS_PLAYER(x, y))
2988     return;
2989
2990   DrawPlayer(PLAYERINFO(x, y));
2991 }
2992
2993 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2994
2995 void DrawPlayer(struct PlayerInfo *player)
2996 {
2997   int jx = player->jx;
2998   int jy = player->jy;
2999   int move_dir = player->MovDir;
3000   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3001   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
3002   int last_jx = (player->is_moving ? jx - dx : jx);
3003   int last_jy = (player->is_moving ? jy - dy : jy);
3004   int next_jx = jx + dx;
3005   int next_jy = jy + dy;
3006   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3007   boolean player_is_opaque = FALSE;
3008   int sx = SCREENX(jx), sy = SCREENY(jy);
3009   int sxx = 0, syy = 0;
3010   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3011   int graphic;
3012   int action = ACTION_DEFAULT;
3013   int last_player_graphic = getPlayerGraphic(player, move_dir);
3014   int last_player_frame = player->Frame;
3015   int frame = 0;
3016
3017   /* GfxElement[][] is set to the element the player is digging or collecting;
3018      remove also for off-screen player if the player is not moving anymore */
3019   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3020     GfxElement[jx][jy] = EL_UNDEFINED;
3021
3022   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3023     return;
3024
3025 #if DEBUG
3026   if (!IN_LEV_FIELD(jx, jy))
3027   {
3028     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3029     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3030     printf("DrawPlayerField(): This should never happen!\n");
3031     return;
3032   }
3033 #endif
3034
3035   if (element == EL_EXPLOSION)
3036     return;
3037
3038   action = (player->is_pushing    ? ACTION_PUSHING         :
3039             player->is_digging    ? ACTION_DIGGING         :
3040             player->is_collecting ? ACTION_COLLECTING      :
3041             player->is_moving     ? ACTION_MOVING          :
3042             player->is_snapping   ? ACTION_SNAPPING        :
3043             player->is_dropping   ? ACTION_DROPPING        :
3044             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
3045
3046   if (player->is_waiting)
3047     move_dir = player->dir_waiting;
3048
3049   InitPlayerGfxAnimation(player, action, move_dir);
3050
3051   /* ----------------------------------------------------------------------- */
3052   /* draw things in the field the player is leaving, if needed               */
3053   /* ----------------------------------------------------------------------- */
3054
3055   if (player->is_moving)
3056   {
3057     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3058     {
3059       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3060
3061       if (last_element == EL_DYNAMITE_ACTIVE ||
3062           last_element == EL_EM_DYNAMITE_ACTIVE ||
3063           last_element == EL_SP_DISK_RED_ACTIVE)
3064         DrawDynamite(last_jx, last_jy);
3065       else
3066         DrawLevelFieldThruMask(last_jx, last_jy);
3067     }
3068     else if (last_element == EL_DYNAMITE_ACTIVE ||
3069              last_element == EL_EM_DYNAMITE_ACTIVE ||
3070              last_element == EL_SP_DISK_RED_ACTIVE)
3071       DrawDynamite(last_jx, last_jy);
3072 #if 0
3073     /* !!! this is not enough to prevent flickering of players which are
3074        moving next to each others without a free tile between them -- this
3075        can only be solved by drawing all players layer by layer (first the
3076        background, then the foreground etc.) !!! => TODO */
3077     else if (!IS_PLAYER(last_jx, last_jy))
3078       DrawLevelField(last_jx, last_jy);
3079 #else
3080     else
3081       DrawLevelField(last_jx, last_jy);
3082 #endif
3083
3084     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3085       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3086   }
3087
3088   if (!IN_SCR_FIELD(sx, sy))
3089     return;
3090
3091   /* ----------------------------------------------------------------------- */
3092   /* draw things behind the player, if needed                                */
3093   /* ----------------------------------------------------------------------- */
3094
3095   if (Back[jx][jy])
3096     DrawLevelElement(jx, jy, Back[jx][jy]);
3097   else if (IS_ACTIVE_BOMB(element))
3098     DrawLevelElement(jx, jy, EL_EMPTY);
3099   else
3100   {
3101     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3102     {
3103       int old_element = GfxElement[jx][jy];
3104       int old_graphic = el_act_dir2img(old_element, action, move_dir);
3105       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3106
3107       if (GFX_CRUMBLED(old_element))
3108         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3109       else
3110         DrawGraphic(sx, sy, old_graphic, frame);
3111
3112       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3113         player_is_opaque = TRUE;
3114     }
3115     else
3116     {
3117       GfxElement[jx][jy] = EL_UNDEFINED;
3118
3119       /* make sure that pushed elements are drawn with correct frame rate */
3120       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3121
3122       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3123         GfxFrame[jx][jy] = player->StepFrame;
3124
3125       DrawLevelField(jx, jy);
3126     }
3127   }
3128
3129 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3130   /* ----------------------------------------------------------------------- */
3131   /* draw player himself                                                     */
3132   /* ----------------------------------------------------------------------- */
3133
3134   graphic = getPlayerGraphic(player, move_dir);
3135
3136   /* in the case of changed player action or direction, prevent the current
3137      animation frame from being restarted for identical animations */
3138   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3139     player->Frame = last_player_frame;
3140
3141   frame = getGraphicAnimationFrame(graphic, player->Frame);
3142
3143   if (player->GfxPos)
3144   {
3145     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3146       sxx = player->GfxPos;
3147     else
3148       syy = player->GfxPos;
3149   }
3150
3151   if (player_is_opaque)
3152     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3153   else
3154     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3155
3156   if (SHIELD_ON(player))
3157   {
3158     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3159                    IMG_SHIELD_NORMAL_ACTIVE);
3160     int frame = getGraphicAnimationFrame(graphic, -1);
3161
3162     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3163   }
3164 #endif
3165
3166 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3167   if (player->GfxPos)
3168   {
3169     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3170       sxx = player->GfxPos;
3171     else
3172       syy = player->GfxPos;
3173   }
3174 #endif
3175
3176   /* ----------------------------------------------------------------------- */
3177   /* draw things the player is pushing, if needed                            */
3178   /* ----------------------------------------------------------------------- */
3179
3180   if (player->is_pushing && player->is_moving)
3181   {
3182     int px = SCREENX(jx), py = SCREENY(jy);
3183     int pxx = (TILEX - ABS(sxx)) * dx;
3184     int pyy = (TILEY - ABS(syy)) * dy;
3185     int gfx_frame = GfxFrame[jx][jy];
3186
3187     int graphic;
3188     int sync_frame;
3189     int frame;
3190
3191     if (!IS_MOVING(jx, jy))             /* push movement already finished */
3192     {
3193       element = Feld[next_jx][next_jy];
3194       gfx_frame = GfxFrame[next_jx][next_jy];
3195     }
3196
3197     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3198
3199     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3200     frame = getGraphicAnimationFrame(graphic, sync_frame);
3201
3202     /* draw background element under pushed element (like the Sokoban field) */
3203     if (game.use_masked_pushing && IS_MOVING(jx, jy))
3204     {
3205       /* this allows transparent pushing animation over non-black background */
3206
3207       if (Back[jx][jy])
3208         DrawLevelElement(jx, jy, Back[jx][jy]);
3209       else
3210         DrawLevelElement(jx, jy, EL_EMPTY);
3211
3212       if (Back[next_jx][next_jy])
3213         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3214       else
3215         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3216     }
3217     else if (Back[next_jx][next_jy])
3218       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3219
3220 #if 1
3221     /* do not draw (EM style) pushing animation when pushing is finished */
3222     /* (two-tile animations usually do not contain start and end frame) */
3223     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3224       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3225     else
3226       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3227 #else
3228     /* masked drawing is needed for EMC style (double) movement graphics */
3229     /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3230     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3231 #endif
3232   }
3233
3234 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3235   /* ----------------------------------------------------------------------- */
3236   /* draw player himself                                                     */
3237   /* ----------------------------------------------------------------------- */
3238
3239   graphic = getPlayerGraphic(player, move_dir);
3240
3241   /* in the case of changed player action or direction, prevent the current
3242      animation frame from being restarted for identical animations */
3243   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3244     player->Frame = last_player_frame;
3245
3246   frame = getGraphicAnimationFrame(graphic, player->Frame);
3247
3248   if (player->GfxPos)
3249   {
3250     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3251       sxx = player->GfxPos;
3252     else
3253       syy = player->GfxPos;
3254   }
3255
3256   if (player_is_opaque)
3257     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3258   else
3259     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3260
3261   if (SHIELD_ON(player))
3262   {
3263     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3264                    IMG_SHIELD_NORMAL_ACTIVE);
3265     int frame = getGraphicAnimationFrame(graphic, -1);
3266
3267     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3268   }
3269 #endif
3270
3271   /* ----------------------------------------------------------------------- */
3272   /* draw things in front of player (active dynamite or dynabombs)           */
3273   /* ----------------------------------------------------------------------- */
3274
3275   if (IS_ACTIVE_BOMB(element))
3276   {
3277     graphic = el2img(element);
3278     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3279
3280     if (game.emulation == EMU_SUPAPLEX)
3281       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3282     else
3283       DrawGraphicThruMask(sx, sy, graphic, frame);
3284   }
3285
3286   if (player_is_moving && last_element == EL_EXPLOSION)
3287   {
3288     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3289                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
3290     int graphic = el_act2img(element, ACTION_EXPLODING);
3291     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3292     int phase = ExplodePhase[last_jx][last_jy] - 1;
3293     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3294
3295     if (phase >= delay)
3296       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3297   }
3298
3299   /* ----------------------------------------------------------------------- */
3300   /* draw elements the player is just walking/passing through/under          */
3301   /* ----------------------------------------------------------------------- */
3302
3303   if (player_is_moving)
3304   {
3305     /* handle the field the player is leaving ... */
3306     if (IS_ACCESSIBLE_INSIDE(last_element))
3307       DrawLevelField(last_jx, last_jy);
3308     else if (IS_ACCESSIBLE_UNDER(last_element))
3309       DrawLevelFieldThruMask(last_jx, last_jy);
3310   }
3311
3312   /* do not redraw accessible elements if the player is just pushing them */
3313   if (!player_is_moving || !player->is_pushing)
3314   {
3315     /* ... and the field the player is entering */
3316     if (IS_ACCESSIBLE_INSIDE(element))
3317       DrawLevelField(jx, jy);
3318     else if (IS_ACCESSIBLE_UNDER(element))
3319       DrawLevelFieldThruMask(jx, jy);
3320   }
3321
3322   MarkTileDirty(sx, sy);
3323 }
3324
3325 /* ------------------------------------------------------------------------- */
3326
3327 void WaitForEventToContinue()
3328 {
3329   boolean still_wait = TRUE;
3330
3331   /* simulate releasing mouse button over last gadget, if still pressed */
3332   if (button_status)
3333     HandleGadgets(-1, -1, 0);
3334
3335   button_status = MB_RELEASED;
3336
3337   ClearEventQueue();
3338
3339   while (still_wait)
3340   {
3341     if (PendingEvent())
3342     {
3343       Event event;
3344
3345       NextEvent(&event);
3346
3347       switch (event.type)
3348       {
3349         case EVENT_BUTTONPRESS:
3350         case EVENT_KEYPRESS:
3351           still_wait = FALSE;
3352           break;
3353
3354         case EVENT_KEYRELEASE:
3355           ClearPlayerAction();
3356           break;
3357
3358         default:
3359           HandleOtherEvents(&event);
3360           break;
3361       }
3362     }
3363     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3364     {
3365       still_wait = FALSE;
3366     }
3367
3368     DoAnimation();
3369
3370     /* don't eat all CPU time */
3371     Delay(10);
3372   }
3373 }
3374
3375 #define MAX_REQUEST_LINES               13
3376 #define MAX_REQUEST_LINE_FONT1_LEN      7
3377 #define MAX_REQUEST_LINE_FONT2_LEN      10
3378
3379 static int RequestHandleEvents(unsigned int req_state)
3380 {
3381   int last_game_status = game_status;   /* save current game status */
3382   int result;
3383   int mx, my;
3384
3385   button_status = MB_RELEASED;
3386
3387   request_gadget_id = -1;
3388   result = -1;
3389
3390   while (result < 0)
3391   {
3392     if (PendingEvent())
3393     {
3394       Event event;
3395
3396       while (NextValidEvent(&event))
3397       {
3398         switch (event.type)
3399         {
3400           case EVENT_BUTTONPRESS:
3401           case EVENT_BUTTONRELEASE:
3402           case EVENT_MOTIONNOTIFY:
3403           {
3404             if (event.type == EVENT_MOTIONNOTIFY)
3405             {
3406               if (!PointerInWindow(window))
3407                 continue;       /* window and pointer on different screens */
3408
3409               if (!button_status)
3410                 continue;
3411
3412               motion_status = TRUE;
3413               mx = ((MotionEvent *) &event)->x;
3414               my = ((MotionEvent *) &event)->y;
3415             }
3416             else
3417             {
3418               motion_status = FALSE;
3419               mx = ((ButtonEvent *) &event)->x;
3420               my = ((ButtonEvent *) &event)->y;
3421               if (event.type == EVENT_BUTTONPRESS)
3422                 button_status = ((ButtonEvent *) &event)->button;
3423               else
3424                 button_status = MB_RELEASED;
3425             }
3426
3427             /* this sets 'request_gadget_id' */
3428             HandleGadgets(mx, my, button_status);
3429
3430             switch (request_gadget_id)
3431             {
3432               case TOOL_CTRL_ID_YES:
3433                 result = TRUE;
3434                 break;
3435               case TOOL_CTRL_ID_NO:
3436                 result = FALSE;
3437                 break;
3438               case TOOL_CTRL_ID_CONFIRM:
3439                 result = TRUE | FALSE;
3440                 break;
3441
3442               case TOOL_CTRL_ID_PLAYER_1:
3443                 result = 1;
3444                 break;
3445               case TOOL_CTRL_ID_PLAYER_2:
3446                 result = 2;
3447                 break;
3448               case TOOL_CTRL_ID_PLAYER_3:
3449                 result = 3;
3450                 break;
3451               case TOOL_CTRL_ID_PLAYER_4:
3452                 result = 4;
3453                 break;
3454
3455               default:
3456                 break;
3457             }
3458
3459             break;
3460           }
3461
3462           case EVENT_KEYPRESS:
3463             switch (GetEventKey((KeyEvent *)&event, TRUE))
3464             {
3465               case KSYM_space:
3466                 if (req_state & REQ_CONFIRM)
3467                   result = 1;
3468                 break;
3469
3470               case KSYM_Return:
3471 #if defined(TARGET_SDL2)
3472               case KSYM_Menu:
3473 #endif
3474                 result = 1;
3475                 break;
3476
3477               case KSYM_Escape:
3478 #if defined(TARGET_SDL2)
3479               case KSYM_Back:
3480 #endif
3481                 result = 0;
3482                 break;
3483
3484               default:
3485                 break;
3486             }
3487
3488             if (req_state & REQ_PLAYER)
3489               result = 0;
3490             break;
3491
3492           case EVENT_KEYRELEASE:
3493             ClearPlayerAction();
3494             break;
3495
3496           default:
3497             HandleOtherEvents(&event);
3498             break;
3499         }
3500       }
3501     }
3502     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3503     {
3504       int joy = AnyJoystick();
3505
3506       if (joy & JOY_BUTTON_1)
3507         result = 1;
3508       else if (joy & JOY_BUTTON_2)
3509         result = 0;
3510     }
3511
3512     if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3513     {
3514       HandleGameActions();
3515     }
3516     else
3517     {
3518       DoAnimation();
3519
3520       if (!PendingEvent())      /* delay only if no pending events */
3521         Delay(10);
3522     }
3523
3524     game_status = GAME_MODE_PSEUDO_DOOR;
3525
3526     BackToFront();
3527
3528     game_status = last_game_status;     /* restore current game status */
3529   }
3530
3531   return result;
3532 }
3533
3534 static boolean RequestDoor(char *text, unsigned int req_state)
3535 {
3536   unsigned int old_door_state;
3537   int last_game_status = game_status;   /* save current game status */
3538   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3539   int font_nr = FONT_TEXT_2;
3540   char *text_ptr;
3541   int result;
3542   int ty;
3543
3544   if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3545   {
3546     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3547     font_nr = FONT_TEXT_1;
3548   }
3549
3550   if (game_status == GAME_MODE_PLAYING)
3551     BlitScreenToBitmap(backbuffer);
3552
3553   /* disable deactivated drawing when quick-loading level tape recording */
3554   if (tape.playing && tape.deactivate_display)
3555     TapeDeactivateDisplayOff(TRUE);
3556
3557   SetMouseCursor(CURSOR_DEFAULT);
3558
3559 #if defined(NETWORK_AVALIABLE)
3560   /* pause network game while waiting for request to answer */
3561   if (options.network &&
3562       game_status == GAME_MODE_PLAYING &&
3563       req_state & REQUEST_WAIT_FOR_INPUT)
3564     SendToServer_PausePlaying();
3565 #endif
3566
3567   old_door_state = GetDoorState();
3568
3569   /* simulate releasing mouse button over last gadget, if still pressed */
3570   if (button_status)
3571     HandleGadgets(-1, -1, 0);
3572
3573   UnmapAllGadgets();
3574
3575   /* draw released gadget before proceeding */
3576   // BackToFront();
3577
3578   if (old_door_state & DOOR_OPEN_1)
3579   {
3580     CloseDoor(DOOR_CLOSE_1);
3581
3582     /* save old door content */
3583     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3584                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3585   }
3586
3587   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3588   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3589
3590   /* clear door drawing field */
3591   DrawBackground(DX, DY, DXSIZE, DYSIZE);
3592
3593   /* force DOOR font inside door area */
3594   game_status = GAME_MODE_PSEUDO_DOOR;
3595
3596   /* write text for request */
3597   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3598   {
3599     char text_line[max_request_line_len + 1];
3600     int tx, tl, tc = 0;
3601
3602     if (!*text_ptr)
3603       break;
3604
3605     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3606     {
3607       tc = *(text_ptr + tx);
3608       // if (!tc || tc == ' ')
3609       if (!tc || tc == ' ' || tc == '?' || tc == '!')
3610         break;
3611     }
3612
3613     if ((tc == '?' || tc == '!') && tl == 0)
3614       tl = 1;
3615
3616     if (!tl)
3617     { 
3618       text_ptr++; 
3619       ty--; 
3620       continue; 
3621     }
3622
3623     strncpy(text_line, text_ptr, tl);
3624     text_line[tl] = 0;
3625
3626     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3627              DY + 8 + ty * (getFontHeight(font_nr) + 2),
3628              text_line, font_nr);
3629
3630     text_ptr += tl + (tc == ' ' ? 1 : 0);
3631     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3632   }
3633
3634   game_status = last_game_status;       /* restore current game status */
3635
3636   if (req_state & REQ_ASK)
3637   {
3638     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3639     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3640   }
3641   else if (req_state & REQ_CONFIRM)
3642   {
3643     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3644   }
3645   else if (req_state & REQ_PLAYER)
3646   {
3647     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3648     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3649     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3650     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3651   }
3652
3653   /* copy request gadgets to door backbuffer */
3654   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3655
3656   OpenDoor(DOOR_OPEN_1);
3657
3658   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3659   {
3660     if (game_status == GAME_MODE_PLAYING)
3661     {
3662       SetPanelBackground();
3663       SetDrawBackgroundMask(REDRAW_DOOR_1);
3664     }
3665     else
3666     {
3667       SetDrawBackgroundMask(REDRAW_FIELD);
3668     }
3669
3670     return FALSE;
3671   }
3672
3673   if (game_status != GAME_MODE_MAIN)
3674     InitAnimation();
3675
3676   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3677
3678   // ---------- handle request buttons ----------
3679   result = RequestHandleEvents(req_state);
3680
3681   if (game_status != GAME_MODE_MAIN)
3682     StopAnimation();
3683
3684   UnmapToolButtons();
3685
3686   if (!(req_state & REQ_STAY_OPEN))
3687   {
3688     CloseDoor(DOOR_CLOSE_1);
3689
3690     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3691         (req_state & REQ_REOPEN))
3692       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3693   }
3694
3695   RemapAllGadgets();
3696
3697   if (game_status == GAME_MODE_PLAYING)
3698   {
3699     SetPanelBackground();
3700     SetDrawBackgroundMask(REDRAW_DOOR_1);
3701   }
3702   else
3703   {
3704     SetDrawBackgroundMask(REDRAW_FIELD);
3705   }
3706
3707 #if defined(NETWORK_AVALIABLE)
3708   /* continue network game after request */
3709   if (options.network &&
3710       game_status == GAME_MODE_PLAYING &&
3711       req_state & REQUEST_WAIT_FOR_INPUT)
3712     SendToServer_ContinuePlaying();
3713 #endif
3714
3715   /* restore deactivated drawing when quick-loading level tape recording */
3716   if (tape.playing && tape.deactivate_display)
3717     TapeDeactivateDisplayOn();
3718
3719   return result;
3720 }
3721
3722 static boolean RequestEnvelope(char *text, unsigned int req_state)
3723 {
3724   int result;
3725
3726   if (game_status == GAME_MODE_PLAYING)
3727     BlitScreenToBitmap(backbuffer);
3728
3729   /* disable deactivated drawing when quick-loading level tape recording */
3730   if (tape.playing && tape.deactivate_display)
3731     TapeDeactivateDisplayOff(TRUE);
3732
3733   SetMouseCursor(CURSOR_DEFAULT);
3734
3735 #if defined(NETWORK_AVALIABLE)
3736   /* pause network game while waiting for request to answer */
3737   if (options.network &&
3738       game_status == GAME_MODE_PLAYING &&
3739       req_state & REQUEST_WAIT_FOR_INPUT)
3740     SendToServer_PausePlaying();
3741 #endif
3742
3743   /* simulate releasing mouse button over last gadget, if still pressed */
3744   if (button_status)
3745     HandleGadgets(-1, -1, 0);
3746
3747   UnmapAllGadgets();
3748
3749   // (replace with setting corresponding request background)
3750   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3751   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3752
3753   /* clear door drawing field */
3754   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3755
3756   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3757
3758   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3759   {
3760     if (game_status == GAME_MODE_PLAYING)
3761     {
3762       SetPanelBackground();
3763       SetDrawBackgroundMask(REDRAW_DOOR_1);
3764     }
3765     else
3766     {
3767       SetDrawBackgroundMask(REDRAW_FIELD);
3768     }
3769
3770     return FALSE;
3771   }
3772
3773   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3774
3775   // ---------- handle request buttons ----------
3776   result = RequestHandleEvents(req_state);
3777
3778   if (game_status != GAME_MODE_MAIN)
3779     StopAnimation();
3780
3781   UnmapToolButtons();
3782
3783   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3784
3785   RemapAllGadgets();
3786
3787   if (game_status == GAME_MODE_PLAYING)
3788   {
3789     SetPanelBackground();
3790     SetDrawBackgroundMask(REDRAW_DOOR_1);
3791   }
3792   else
3793   {
3794     SetDrawBackgroundMask(REDRAW_FIELD);
3795   }
3796
3797 #if defined(NETWORK_AVALIABLE)
3798   /* continue network game after request */
3799   if (options.network &&
3800       game_status == GAME_MODE_PLAYING &&
3801       req_state & REQUEST_WAIT_FOR_INPUT)
3802     SendToServer_ContinuePlaying();
3803 #endif
3804
3805   /* restore deactivated drawing when quick-loading level tape recording */
3806   if (tape.playing && tape.deactivate_display)
3807     TapeDeactivateDisplayOn();
3808
3809   return result;
3810 }
3811
3812 boolean Request(char *text, unsigned int req_state)
3813 {
3814   if (global.use_envelope_request)
3815     return RequestEnvelope(text, req_state);
3816   else
3817     return RequestDoor(text, req_state);
3818 }
3819
3820 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3821 {
3822   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3823   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3824   int compare_result;
3825
3826   if (dpo1->sort_priority != dpo2->sort_priority)
3827     compare_result = dpo1->sort_priority - dpo2->sort_priority;
3828   else
3829     compare_result = dpo1->nr - dpo2->nr;
3830
3831   return compare_result;
3832 }
3833
3834 void InitGraphicCompatibilityInfo_Doors()
3835 {
3836   struct
3837   {
3838     int door_token;
3839     int part_1, part_8;
3840     struct DoorInfo *door;
3841   }
3842   doors[] =
3843   {
3844     { DOOR_1,   IMG_DOOR_1_GFX_PART_1,  IMG_DOOR_1_GFX_PART_8,  &door_1 },
3845     { DOOR_2,   IMG_DOOR_2_GFX_PART_1,  IMG_DOOR_2_GFX_PART_8,  &door_2 },
3846
3847     { -1,       -1,                     -1,                     NULL    }
3848   };
3849   struct Rect door_rect_list[] =
3850   {
3851     { DX, DY, DXSIZE, DYSIZE },
3852     { VX, VY, VXSIZE, VYSIZE }
3853   };
3854   int i, j;
3855
3856   for (i = 0; doors[i].door_token != -1; i++)
3857   {
3858     int door_token = doors[i].door_token;
3859     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3860     int part_1 = doors[i].part_1;
3861     int part_8 = doors[i].part_8;
3862     int part_2 = part_1 + 1;
3863     int part_3 = part_1 + 2;
3864     struct DoorInfo *door = doors[i].door;
3865     struct Rect *door_rect = &door_rect_list[door_index];
3866     boolean door_gfx_redefined = FALSE;
3867
3868     /* check if any door part graphic definitions have been redefined */
3869
3870     for (j = 0; door_part_controls[j].door_token != -1; j++)
3871     {
3872       struct DoorPartControlInfo *dpc = &door_part_controls[j];
3873       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3874
3875       if (dpc->door_token == door_token && fi->redefined)
3876         door_gfx_redefined = TRUE;
3877     }
3878
3879     /* check for old-style door graphic/animation modifications */
3880
3881     if (!door_gfx_redefined)
3882     {
3883       if (door->anim_mode & ANIM_STATIC_PANEL)
3884       {
3885         door->panel.step_xoffset = 0;
3886         door->panel.step_yoffset = 0;
3887       }
3888
3889       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3890       {
3891         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3892         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3893         int num_door_steps, num_panel_steps;
3894
3895         /* remove door part graphics other than the two default wings */
3896
3897         for (j = 0; door_part_controls[j].door_token != -1; j++)
3898         {
3899           struct DoorPartControlInfo *dpc = &door_part_controls[j];
3900           struct GraphicInfo *g = &graphic_info[dpc->graphic];
3901
3902           if (dpc->graphic >= part_3 &&
3903               dpc->graphic <= part_8)
3904             g->bitmap = NULL;
3905         }
3906
3907         /* set graphics and screen positions of the default wings */
3908
3909         g_part_1->width  = door_rect->width;
3910         g_part_1->height = door_rect->height;
3911         g_part_2->width  = door_rect->width;
3912         g_part_2->height = door_rect->height;
3913         g_part_2->src_x = door_rect->width;
3914         g_part_2->src_y = g_part_1->src_y;
3915
3916         door->part_2.x = door->part_1.x;
3917         door->part_2.y = door->part_1.y;
3918
3919         if (door->width != -1)
3920         {
3921           g_part_1->width = door->width;
3922           g_part_2->width = door->width;
3923
3924           // special treatment for graphics and screen position of right wing
3925           g_part_2->src_x += door_rect->width - door->width;
3926           door->part_2.x  += door_rect->width - door->width;
3927         }
3928
3929         if (door->height != -1)
3930         {
3931           g_part_1->height = door->height;
3932           g_part_2->height = door->height;
3933
3934           // special treatment for graphics and screen position of bottom wing
3935           g_part_2->src_y += door_rect->height - door->height;
3936           door->part_2.y  += door_rect->height - door->height;
3937         }
3938
3939         /* set animation delays for the default wings and panels */
3940
3941         door->part_1.step_delay = door->step_delay;
3942         door->part_2.step_delay = door->step_delay;
3943         door->panel.step_delay  = door->step_delay;
3944
3945         /* set animation draw order for the default wings */
3946
3947         door->part_1.sort_priority = 2; /* draw left wing over ... */
3948         door->part_2.sort_priority = 1; /*          ... right wing */
3949
3950         /* set animation draw offset for the default wings */
3951
3952         if (door->anim_mode & ANIM_HORIZONTAL)
3953         {
3954           door->part_1.step_xoffset = door->step_offset;
3955           door->part_1.step_yoffset = 0;
3956           door->part_2.step_xoffset = door->step_offset * -1;
3957           door->part_2.step_yoffset = 0;
3958
3959           num_door_steps = g_part_1->width / door->step_offset;
3960         }
3961         else    // ANIM_VERTICAL
3962         {
3963           door->part_1.step_xoffset = 0;
3964           door->part_1.step_yoffset = door->step_offset;
3965           door->part_2.step_xoffset = 0;
3966           door->part_2.step_yoffset = door->step_offset * -1;
3967
3968           num_door_steps = g_part_1->height / door->step_offset;
3969         }
3970
3971         /* set animation draw offset for the default panels */
3972
3973         if (door->step_offset > 1)
3974         {
3975           num_panel_steps = 2 * door_rect->height / door->step_offset;
3976           door->panel.start_step = num_panel_steps - num_door_steps;
3977           door->panel.start_step_closing = door->panel.start_step;
3978         }
3979         else
3980         {
3981           num_panel_steps = door_rect->height / door->step_offset;
3982           door->panel.start_step = num_panel_steps - num_door_steps / 2;
3983           door->panel.start_step_closing = door->panel.start_step;
3984           door->panel.step_delay *= 2;
3985         }
3986       }
3987     }
3988   }
3989 }
3990
3991 void InitDoors()
3992 {
3993   int i;
3994
3995   for (i = 0; door_part_controls[i].door_token != -1; i++)
3996   {
3997     struct DoorPartControlInfo *dpc = &door_part_controls[i];
3998     struct DoorPartOrderInfo *dpo = &door_part_order[i];
3999
4000     /* initialize "start_step_opening" and "start_step_closing", if needed */
4001     if (dpc->pos->start_step_opening == 0 &&
4002         dpc->pos->start_step_closing == 0)
4003     {
4004       // dpc->pos->start_step_opening = dpc->pos->start_step;
4005       dpc->pos->start_step_closing = dpc->pos->start_step;
4006     }
4007
4008     /* fill structure for door part draw order (sorted below) */
4009     dpo->nr = i;
4010     dpo->sort_priority = dpc->pos->sort_priority;
4011   }
4012
4013   /* sort door part controls according to sort_priority and graphic number */
4014   qsort(door_part_order, MAX_DOOR_PARTS,
4015         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4016 }
4017
4018 unsigned int OpenDoor(unsigned int door_state)
4019 {
4020   if (door_state & DOOR_COPY_BACK)
4021   {
4022     if (door_state & DOOR_OPEN_1)
4023       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4024                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4025
4026     if (door_state & DOOR_OPEN_2)
4027       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4028                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4029
4030     door_state &= ~DOOR_COPY_BACK;
4031   }
4032
4033   return MoveDoor(door_state);
4034 }
4035
4036 unsigned int CloseDoor(unsigned int door_state)
4037 {
4038   unsigned int old_door_state = GetDoorState();
4039
4040   if (!(door_state & DOOR_NO_COPY_BACK))
4041   {
4042     if (old_door_state & DOOR_OPEN_1)
4043       BlitBitmap(backbuffer, bitmap_db_door_1,
4044                  DX, DY, DXSIZE, DYSIZE, 0, 0);
4045
4046     if (old_door_state & DOOR_OPEN_2)
4047       BlitBitmap(backbuffer, bitmap_db_door_2,
4048                  VX, VY, VXSIZE, VYSIZE, 0, 0);
4049
4050     door_state &= ~DOOR_NO_COPY_BACK;
4051   }
4052
4053   return MoveDoor(door_state);
4054 }
4055
4056 unsigned int GetDoorState()
4057 {
4058   return MoveDoor(DOOR_GET_STATE);
4059 }
4060
4061 unsigned int SetDoorState(unsigned int door_state)
4062 {
4063   return MoveDoor(door_state | DOOR_SET_STATE);
4064 }
4065
4066 int euclid(int a, int b)
4067 {
4068   return (b ? euclid(b, a % b) : a);
4069 }
4070
4071 unsigned int MoveDoor(unsigned int door_state)
4072 {
4073   struct Rect door_rect_list[] =
4074   {
4075     { DX, DY, DXSIZE, DYSIZE },
4076     { VX, VY, VXSIZE, VYSIZE }
4077   };
4078   static int door1 = DOOR_OPEN_1;
4079   static int door2 = DOOR_CLOSE_2;
4080   unsigned int door_delay = 0;
4081   unsigned int door_delay_value;
4082   int i;
4083
4084   if (door_state == DOOR_GET_STATE)
4085     return (door1 | door2);
4086
4087   if (door_state & DOOR_SET_STATE)
4088   {
4089     if (door_state & DOOR_ACTION_1)
4090       door1 = door_state & DOOR_ACTION_1;
4091     if (door_state & DOOR_ACTION_2)
4092       door2 = door_state & DOOR_ACTION_2;
4093
4094     return (door1 | door2);
4095   }
4096
4097   if (!(door_state & DOOR_FORCE_REDRAW))
4098   {
4099     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4100       door_state &= ~DOOR_OPEN_1;
4101     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4102       door_state &= ~DOOR_CLOSE_1;
4103     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4104       door_state &= ~DOOR_OPEN_2;
4105     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4106       door_state &= ~DOOR_CLOSE_2;
4107   }
4108
4109   if (global.autoplay_leveldir)
4110   {
4111     door_state |= DOOR_NO_DELAY;
4112     door_state &= ~DOOR_CLOSE_ALL;
4113   }
4114
4115   if (game_status == GAME_MODE_EDITOR)
4116     door_state |= DOOR_NO_DELAY;
4117
4118   if (door_state & DOOR_ACTION)
4119   {
4120     boolean door_panel_drawn[NUM_DOORS];
4121     boolean panel_has_doors[NUM_DOORS];
4122     boolean door_part_skip[MAX_DOOR_PARTS];
4123     boolean door_part_done[MAX_DOOR_PARTS];
4124     boolean door_part_done_all;
4125     int num_steps[MAX_DOOR_PARTS];
4126     int max_move_delay = 0;     // delay for complete animations of all doors
4127     int max_step_delay = 0;     // delay (ms) between two animation frames
4128     int num_move_steps = 0;     // number of animation steps for all doors
4129     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
4130     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
4131     int current_move_delay = 0;
4132     int start = 0;
4133     int k;
4134
4135     for (i = 0; i < NUM_DOORS; i++)
4136       panel_has_doors[i] = FALSE;
4137
4138     for (i = 0; i < MAX_DOOR_PARTS; i++)
4139     {
4140       struct DoorPartControlInfo *dpc = &door_part_controls[i];
4141       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4142       int door_token = dpc->door_token;
4143
4144       door_part_done[i] = FALSE;
4145       door_part_skip[i] = (!(door_state & door_token) ||
4146                            !g->bitmap);
4147     }
4148
4149     for (i = 0; i < MAX_DOOR_PARTS; i++)
4150     {
4151       int nr = door_part_order[i].nr;
4152       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4153       struct DoorPartPosInfo *pos = dpc->pos;
4154       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4155       int door_token = dpc->door_token;
4156       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4157       boolean is_panel = DOOR_PART_IS_PANEL(nr);
4158       int step_xoffset = ABS(pos->step_xoffset);
4159       int step_yoffset = ABS(pos->step_yoffset);
4160       int step_delay = pos->step_delay;
4161       int current_door_state = door_state & door_token;
4162       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4163       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4164       boolean part_opening = (is_panel ? door_closing : door_opening);
4165       int start_step = (part_opening ? pos->start_step_opening :
4166                         pos->start_step_closing);
4167       float move_xsize = (step_xoffset ? g->width  : 0);
4168       float move_ysize = (step_yoffset ? g->height : 0);
4169       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4170       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4171       int move_steps = (move_xsteps && move_ysteps ?
4172                         MIN(move_xsteps, move_ysteps) :
4173                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
4174       int move_delay = move_steps * step_delay;
4175
4176       if (door_part_skip[nr])
4177         continue;
4178
4179       max_move_delay = MAX(max_move_delay, move_delay);
4180       max_step_delay = (max_step_delay == 0 ? step_delay :
4181                         euclid(max_step_delay, step_delay));
4182       num_steps[nr] = move_steps;
4183
4184       if (!is_panel)
4185       {
4186         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4187
4188         panel_has_doors[door_index] = TRUE;
4189       }
4190     }
4191
4192     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
4193
4194     num_move_steps = max_move_delay / max_step_delay;
4195     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4196
4197     door_delay_value = max_step_delay;
4198
4199     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4200     {
4201       start = num_move_steps - 1;
4202     }
4203     else
4204     {
4205       /* opening door sound has priority over simultaneously closing door */
4206       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4207         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4208       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4209         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4210     }
4211
4212     for (k = start; k < num_move_steps; k++)
4213     {
4214       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
4215
4216       door_part_done_all = TRUE;
4217
4218       for (i = 0; i < NUM_DOORS; i++)
4219         door_panel_drawn[i] = FALSE;
4220
4221       for (i = 0; i < MAX_DOOR_PARTS; i++)
4222       {
4223         int nr = door_part_order[i].nr;
4224         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4225         struct DoorPartPosInfo *pos = dpc->pos;
4226         struct GraphicInfo *g = &graphic_info[dpc->graphic];
4227         int door_token = dpc->door_token;
4228         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4229         boolean is_panel = DOOR_PART_IS_PANEL(nr);
4230         boolean is_panel_and_door_has_closed = FALSE;
4231         struct Rect *door_rect = &door_rect_list[door_index];
4232         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4233                                   bitmap_db_door_2);
4234         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4235         int current_door_state = door_state & door_token;
4236         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4237         boolean door_closing = !door_opening;
4238         boolean part_opening = (is_panel ? door_closing : door_opening);
4239         boolean part_closing = !part_opening;
4240         int start_step = (part_opening ? pos->start_step_opening :
4241                           pos->start_step_closing);
4242         int step_delay = pos->step_delay;
4243         int step_factor = step_delay / max_step_delay;
4244         int k1 = (step_factor ? k / step_factor + 1 : k);
4245         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4246         int kk = MAX(0, k2);
4247         int g_src_x = 0;
4248         int g_src_y = 0;
4249         int src_x, src_y, src_xx, src_yy;
4250         int dst_x, dst_y, dst_xx, dst_yy;
4251         int width, height;
4252
4253         if (door_part_skip[nr])
4254           continue;
4255
4256         if (!(door_state & door_token))
4257           continue;
4258
4259         if (!g->bitmap)
4260           continue;
4261
4262         if (!is_panel)
4263         {
4264           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4265           int kk_door = MAX(0, k2_door);
4266           int sync_frame = kk_door * door_delay_value;
4267           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4268
4269           getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4270         }
4271
4272         // draw door panel
4273
4274         if (!door_panel_drawn[door_index])
4275         {
4276           ClearRectangle(drawto, door_rect->x, door_rect->y,
4277                          door_rect->width, door_rect->height);
4278
4279           door_panel_drawn[door_index] = TRUE;
4280         }
4281
4282         // draw opening or closing door parts
4283
4284         if (pos->step_xoffset < 0)      // door part on right side
4285         {
4286           src_xx = 0;
4287           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4288           width = g->width;
4289
4290           if (dst_xx + width > door_rect->width)
4291             width = door_rect->width - dst_xx;
4292         }
4293         else                            // door part on left side
4294         {
4295           src_xx = 0;
4296           dst_xx = pos->x - kk * pos->step_xoffset;
4297
4298           if (dst_xx < 0)
4299           {
4300             src_xx = ABS(dst_xx);
4301             dst_xx = 0;
4302           }
4303
4304           width = g->width - src_xx;
4305
4306           // printf("::: k == %d [%d] \n", k, start_step);
4307         }
4308
4309         if (pos->step_yoffset < 0)      // door part on bottom side
4310         {
4311           src_yy = 0;
4312           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4313           height = g->height;
4314
4315           if (dst_yy + height > door_rect->height)
4316             height = door_rect->height - dst_yy;
4317         }
4318         else                            // door part on top side
4319         {
4320           src_yy = 0;
4321           dst_yy = pos->y - kk * pos->step_yoffset;
4322
4323           if (dst_yy < 0)
4324           {
4325             src_yy = ABS(dst_yy);
4326             dst_yy = 0;
4327           }
4328
4329           height = g->height - src_yy;
4330         }
4331
4332         src_x = g_src_x + src_xx;
4333         src_y = g_src_y + src_yy;
4334
4335         dst_x = door_rect->x + dst_xx;
4336         dst_y = door_rect->y + dst_yy;
4337
4338         is_panel_and_door_has_closed =
4339           (is_panel &&
4340            door_closing &&
4341            panel_has_doors[door_index] &&
4342            k >= num_move_steps_doors_only - 1);
4343
4344         if (width  >= 0 && width  <= g->width &&
4345             height >= 0 && height <= g->height &&
4346             !is_panel_and_door_has_closed)
4347         {
4348           if (is_panel || !pos->draw_masked)
4349             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4350                        dst_x, dst_y);
4351           else
4352             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4353                              dst_x, dst_y);
4354         }
4355
4356         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4357
4358         if ((part_opening && (width < 0         || height < 0)) ||
4359             (part_closing && (width >= g->width && height >= g->height)))
4360           door_part_done[nr] = TRUE;
4361
4362         // continue door part animations, but not panel after door has closed
4363         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4364           door_part_done_all = FALSE;
4365       }
4366
4367       if (!(door_state & DOOR_NO_DELAY))
4368       {
4369         BackToFront();
4370
4371         if (game_status == GAME_MODE_MAIN)
4372           DoAnimation();
4373
4374         SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4375
4376         current_move_delay += max_step_delay;
4377       }
4378
4379       if (door_part_done_all)
4380         break;
4381     }
4382   }
4383
4384   if (door_state & DOOR_ACTION_1)
4385     door1 = door_state & DOOR_ACTION_1;
4386   if (door_state & DOOR_ACTION_2)
4387     door2 = door_state & DOOR_ACTION_2;
4388
4389   return (door1 | door2);
4390 }
4391
4392 void DrawSpecialEditorDoor()
4393 {
4394   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4395   int top_border_width = gfx1->width;
4396   int top_border_height = gfx1->height;
4397   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4398   int ex = EX - outer_border;
4399   int ey = EY - outer_border;
4400   int vy = VY - outer_border;
4401   int exsize = EXSIZE + 2 * outer_border;
4402
4403   /* draw bigger level editor toolbox window */
4404   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4405              top_border_width, top_border_height, ex, ey - top_border_height);
4406   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4407              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4408
4409   redraw_mask |= REDRAW_ALL;
4410 }
4411
4412 void UndrawSpecialEditorDoor()
4413 {
4414   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4415   int top_border_width = gfx1->width;
4416   int top_border_height = gfx1->height;
4417   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4418   int ex = EX - outer_border;
4419   int ey = EY - outer_border;
4420   int ey_top = ey - top_border_height;
4421   int exsize = EXSIZE + 2 * outer_border;
4422   int eysize = EYSIZE + 2 * outer_border;
4423
4424   /* draw normal tape recorder window */
4425   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4426   {
4427     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4428                ex, ey_top, top_border_width, top_border_height,
4429                ex, ey_top);
4430     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4431                ex, ey, exsize, eysize, ex, ey);
4432   }
4433   else
4434   {
4435     // if screen background is set to "[NONE]", clear editor toolbox window
4436     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4437     ClearRectangle(drawto, ex, ey, exsize, eysize);
4438   }
4439
4440   redraw_mask |= REDRAW_ALL;
4441 }
4442
4443
4444 /* ---------- new tool button stuff ---------------------------------------- */
4445
4446 static struct
4447 {
4448   int graphic;
4449   struct TextPosInfo *pos;
4450   int gadget_id;
4451   char *infotext;
4452 } toolbutton_info[NUM_TOOL_BUTTONS] =
4453 {
4454   {
4455     IMG_REQUEST_BUTTON_GFX_YES,         &request.button.yes,
4456     TOOL_CTRL_ID_YES,                   "yes"
4457   },
4458   {
4459     IMG_REQUEST_BUTTON_GFX_NO,          &request.button.no,
4460     TOOL_CTRL_ID_NO,                    "no"
4461   },
4462   {
4463     IMG_REQUEST_BUTTON_GFX_CONFIRM,     &request.button.confirm,
4464     TOOL_CTRL_ID_CONFIRM,               "confirm"
4465   },
4466   {
4467     IMG_REQUEST_BUTTON_GFX_PLAYER_1,    &request.button.player_1,
4468     TOOL_CTRL_ID_PLAYER_1,              "player 1"
4469   },
4470   {
4471     IMG_REQUEST_BUTTON_GFX_PLAYER_2,    &request.button.player_2,
4472     TOOL_CTRL_ID_PLAYER_2,              "player 2"
4473   },
4474   {
4475     IMG_REQUEST_BUTTON_GFX_PLAYER_3,    &request.button.player_3,
4476     TOOL_CTRL_ID_PLAYER_3,              "player 3"
4477   },
4478   {
4479     IMG_REQUEST_BUTTON_GFX_PLAYER_4,    &request.button.player_4,
4480     TOOL_CTRL_ID_PLAYER_4,              "player 4"
4481   }
4482 };
4483
4484 void CreateToolButtons()
4485 {
4486   int i;
4487
4488   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4489   {
4490     struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4491     struct TextPosInfo *pos = toolbutton_info[i].pos;
4492     struct GadgetInfo *gi;
4493     Bitmap *deco_bitmap = None;
4494     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4495     unsigned int event_mask = GD_EVENT_RELEASED;
4496     int dx = DX;
4497     int dy = DY;
4498     int gd_x = gfx->src_x;
4499     int gd_y = gfx->src_y;
4500     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4501     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4502     int id = i;
4503
4504     if (global.use_envelope_request)
4505       setRequestPosition(&dx, &dy, TRUE);
4506
4507     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4508     {
4509       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4510
4511       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4512                             pos->size, &deco_bitmap, &deco_x, &deco_y);
4513       deco_xpos = (gfx->width  - pos->size) / 2;
4514       deco_ypos = (gfx->height - pos->size) / 2;
4515     }
4516
4517     gi = CreateGadget(GDI_CUSTOM_ID, id,
4518                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
4519                       GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4520                       GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4521                       GDI_WIDTH, gfx->width,
4522                       GDI_HEIGHT, gfx->height,
4523                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4524                       GDI_STATE, GD_BUTTON_UNPRESSED,
4525                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4526                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4527                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4528                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4529                       GDI_DECORATION_SIZE, pos->size, pos->size,
4530                       GDI_DECORATION_SHIFTING, 1, 1,
4531                       GDI_DIRECT_DRAW, FALSE,
4532                       GDI_EVENT_MASK, event_mask,
4533                       GDI_CALLBACK_ACTION, HandleToolButtons,
4534                       GDI_END);
4535
4536     if (gi == NULL)
4537       Error(ERR_EXIT, "cannot create gadget");
4538
4539     tool_gadget[id] = gi;
4540   }
4541 }
4542
4543 void FreeToolButtons()
4544 {
4545   int i;
4546
4547   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4548     FreeGadget(tool_gadget[i]);
4549 }
4550
4551 static void UnmapToolButtons()
4552 {
4553   int i;
4554
4555   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4556     UnmapGadget(tool_gadget[i]);
4557 }
4558
4559 static void HandleToolButtons(struct GadgetInfo *gi)
4560 {
4561   request_gadget_id = gi->custom_id;
4562 }
4563
4564 static struct Mapping_EM_to_RND_object
4565 {
4566   int element_em;
4567   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
4568   boolean is_backside;                  /* backside of moving element */
4569
4570   int element_rnd;
4571   int action;
4572   int direction;
4573 }
4574 em_object_mapping_list[] =
4575 {
4576   {
4577     Xblank,                             TRUE,   FALSE,
4578     EL_EMPTY,                           -1, -1
4579   },
4580   {
4581     Yacid_splash_eB,                    FALSE,  FALSE,
4582     EL_ACID_SPLASH_RIGHT,               -1, -1
4583   },
4584   {
4585     Yacid_splash_wB,                    FALSE,  FALSE,
4586     EL_ACID_SPLASH_LEFT,                -1, -1
4587   },
4588
4589 #ifdef EM_ENGINE_BAD_ROLL
4590   {
4591     Xstone_force_e,                     FALSE,  FALSE,
4592     EL_ROCK,                            -1, MV_BIT_RIGHT
4593   },
4594   {
4595     Xstone_force_w,                     FALSE,  FALSE,
4596     EL_ROCK,                            -1, MV_BIT_LEFT
4597   },
4598   {
4599     Xnut_force_e,                       FALSE,  FALSE,
4600     EL_NUT,                             -1, MV_BIT_RIGHT
4601   },
4602   {
4603     Xnut_force_w,                       FALSE,  FALSE,
4604     EL_NUT,                             -1, MV_BIT_LEFT
4605   },
4606   {
4607     Xspring_force_e,                    FALSE,  FALSE,
4608     EL_SPRING,                          -1, MV_BIT_RIGHT
4609   },
4610   {
4611     Xspring_force_w,                    FALSE,  FALSE,
4612     EL_SPRING,                          -1, MV_BIT_LEFT
4613   },
4614   {
4615     Xemerald_force_e,                   FALSE,  FALSE,
4616     EL_EMERALD,                         -1, MV_BIT_RIGHT
4617   },
4618   {
4619     Xemerald_force_w,                   FALSE,  FALSE,
4620     EL_EMERALD,                         -1, MV_BIT_LEFT
4621   },
4622   {
4623     Xdiamond_force_e,                   FALSE,  FALSE,
4624     EL_DIAMOND,                         -1, MV_BIT_RIGHT
4625   },
4626   {
4627     Xdiamond_force_w,                   FALSE,  FALSE,
4628     EL_DIAMOND,                         -1, MV_BIT_LEFT
4629   },
4630   {
4631     Xbomb_force_e,                      FALSE,  FALSE,
4632     EL_BOMB,                            -1, MV_BIT_RIGHT
4633   },
4634   {
4635     Xbomb_force_w,                      FALSE,  FALSE,
4636     EL_BOMB,                            -1, MV_BIT_LEFT
4637   },
4638 #endif  /* EM_ENGINE_BAD_ROLL */
4639
4640   {
4641     Xstone,                             TRUE,   FALSE,
4642     EL_ROCK,                            -1, -1
4643   },
4644   {
4645     Xstone_pause,                       FALSE,  FALSE,
4646     EL_ROCK,                            -1, -1
4647   },
4648   {
4649     Xstone_fall,                        FALSE,  FALSE,
4650     EL_ROCK,                            -1, -1
4651   },
4652   {
4653     Ystone_s,                           FALSE,  FALSE,
4654     EL_ROCK,                            ACTION_FALLING, -1
4655   },
4656   {
4657     Ystone_sB,                          FALSE,  TRUE,
4658     EL_ROCK,                            ACTION_FALLING, -1
4659   },
4660   {
4661     Ystone_e,                           FALSE,  FALSE,
4662     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
4663   },
4664   {
4665     Ystone_eB,                          FALSE,  TRUE,
4666     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
4667   },
4668   {
4669     Ystone_w,                           FALSE,  FALSE,
4670     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
4671   },
4672   {
4673     Ystone_wB,                          FALSE,  TRUE,
4674     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
4675   },
4676   {
4677     Xnut,                               TRUE,   FALSE,
4678     EL_NUT,                             -1, -1
4679   },
4680   {
4681     Xnut_pause,                         FALSE,  FALSE,
4682     EL_NUT,                             -1, -1
4683   },
4684   {
4685     Xnut_fall,                          FALSE,  FALSE,
4686     EL_NUT,                             -1, -1
4687   },
4688   {
4689     Ynut_s,                             FALSE,  FALSE,
4690     EL_NUT,                             ACTION_FALLING, -1
4691   },
4692   {
4693     Ynut_sB,                            FALSE,  TRUE,
4694     EL_NUT,                             ACTION_FALLING, -1
4695   },
4696   {
4697     Ynut_e,                             FALSE,  FALSE,
4698     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
4699   },
4700   {
4701     Ynut_eB,                            FALSE,  TRUE,
4702     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
4703   },
4704   {
4705     Ynut_w,                             FALSE,  FALSE,
4706     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
4707   },
4708   {
4709     Ynut_wB,                            FALSE,  TRUE,
4710     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
4711   },
4712   {
4713     Xbug_n,                             TRUE,   FALSE,
4714     EL_BUG_UP,                          -1, -1
4715   },
4716   {
4717     Xbug_e,                             TRUE,   FALSE,
4718     EL_BUG_RIGHT,                       -1, -1
4719   },
4720   {
4721     Xbug_s,                             TRUE,   FALSE,
4722     EL_BUG_DOWN,                        -1, -1
4723   },
4724   {
4725     Xbug_w,                             TRUE,   FALSE,
4726     EL_BUG_LEFT,                        -1, -1
4727   },
4728   {
4729     Xbug_gon,                           FALSE,  FALSE,
4730     EL_BUG_UP,                          -1, -1
4731   },
4732   {
4733     Xbug_goe,                           FALSE,  FALSE,
4734     EL_BUG_RIGHT,                       -1, -1
4735   },
4736   {
4737     Xbug_gos,                           FALSE,  FALSE,
4738     EL_BUG_DOWN,                        -1, -1
4739   },
4740   {
4741     Xbug_gow,                           FALSE,  FALSE,
4742     EL_BUG_LEFT,                        -1, -1
4743   },
4744   {
4745     Ybug_n,                             FALSE,  FALSE,
4746     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
4747   },
4748   {
4749     Ybug_nB,                            FALSE,  TRUE,
4750     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
4751   },
4752   {
4753     Ybug_e,                             FALSE,  FALSE,
4754     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
4755   },
4756   {
4757     Ybug_eB,                            FALSE,  TRUE,
4758     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
4759   },
4760   {
4761     Ybug_s,                             FALSE,  FALSE,
4762     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
4763   },
4764   {
4765     Ybug_sB,                            FALSE,  TRUE,
4766     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
4767   },
4768   {
4769     Ybug_w,                             FALSE,  FALSE,
4770     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
4771   },
4772   {
4773     Ybug_wB,                            FALSE,  TRUE,
4774     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
4775   },
4776   {
4777     Ybug_w_n,                           FALSE,  FALSE,
4778     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4779   },
4780   {
4781     Ybug_n_e,                           FALSE,  FALSE,
4782     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4783   },
4784   {
4785     Ybug_e_s,                           FALSE,  FALSE,
4786     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4787   },
4788   {
4789     Ybug_s_w,                           FALSE,  FALSE,
4790     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4791   },
4792   {
4793     Ybug_e_n,                           FALSE,  FALSE,
4794     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4795   },
4796   {
4797     Ybug_s_e,                           FALSE,  FALSE,
4798     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4799   },
4800   {
4801     Ybug_w_s,                           FALSE,  FALSE,
4802     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4803   },
4804   {
4805     Ybug_n_w,                           FALSE,  FALSE,
4806     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4807   },
4808   {
4809     Ybug_stone,                         FALSE,  FALSE,
4810     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
4811   },
4812   {
4813     Ybug_spring,                        FALSE,  FALSE,
4814     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
4815   },
4816   {
4817     Xtank_n,                            TRUE,   FALSE,
4818     EL_SPACESHIP_UP,                    -1, -1
4819   },
4820   {
4821     Xtank_e,                            TRUE,   FALSE,
4822     EL_SPACESHIP_RIGHT,                 -1, -1
4823   },
4824   {
4825     Xtank_s,                            TRUE,   FALSE,
4826     EL_SPACESHIP_DOWN,                  -1, -1
4827   },
4828   {
4829     Xtank_w,                            TRUE,   FALSE,
4830     EL_SPACESHIP_LEFT,                  -1, -1
4831   },
4832   {
4833     Xtank_gon,                          FALSE,  FALSE,
4834     EL_SPACESHIP_UP,                    -1, -1
4835   },
4836   {
4837     Xtank_goe,                          FALSE,  FALSE,
4838     EL_SPACESHIP_RIGHT,                 -1, -1
4839   },
4840   {
4841     Xtank_gos,                          FALSE,  FALSE,
4842     EL_SPACESHIP_DOWN,                  -1, -1
4843   },
4844   {
4845     Xtank_gow,                          FALSE,  FALSE,
4846     EL_SPACESHIP_LEFT,                  -1, -1
4847   },
4848   {
4849     Ytank_n,                            FALSE,  FALSE,
4850     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
4851   },
4852   {
4853     Ytank_nB,                           FALSE,  TRUE,
4854     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
4855   },
4856   {
4857     Ytank_e,                            FALSE,  FALSE,
4858     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
4859   },
4860   {
4861     Ytank_eB,                           FALSE,  TRUE,
4862     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
4863   },
4864   {
4865     Ytank_s,                            FALSE,  FALSE,
4866     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
4867   },
4868   {
4869     Ytank_sB,                           FALSE,  TRUE,
4870     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
4871   },
4872   {
4873     Ytank_w,                            FALSE,  FALSE,
4874     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
4875   },
4876   {
4877     Ytank_wB,                           FALSE,  TRUE,
4878     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
4879   },
4880   {
4881     Ytank_w_n,                          FALSE,  FALSE,
4882     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4883   },
4884   {
4885     Ytank_n_e,                          FALSE,  FALSE,
4886     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4887   },
4888   {
4889     Ytank_e_s,                          FALSE,  FALSE,
4890     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4891   },
4892   {
4893     Ytank_s_w,                          FALSE,  FALSE,
4894     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4895   },
4896   {
4897     Ytank_e_n,                          FALSE,  FALSE,
4898     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4899   },
4900   {
4901     Ytank_s_e,                          FALSE,  FALSE,
4902     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4903   },
4904   {
4905     Ytank_w_s,                          FALSE,  FALSE,
4906     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4907   },
4908   {
4909     Ytank_n_w,                          FALSE,  FALSE,
4910     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4911   },
4912   {
4913     Ytank_stone,                        FALSE,  FALSE,
4914     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
4915   },
4916   {
4917     Ytank_spring,                       FALSE,  FALSE,
4918     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
4919   },
4920   {
4921     Xandroid,                           TRUE,   FALSE,
4922     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
4923   },
4924   {
4925     Xandroid_1_n,                       FALSE,  FALSE,
4926     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
4927   },
4928   {
4929     Xandroid_2_n,                       FALSE,  FALSE,
4930     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
4931   },
4932   {
4933     Xandroid_1_e,                       FALSE,  FALSE,
4934     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
4935   },
4936   {
4937     Xandroid_2_e,                       FALSE,  FALSE,
4938     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
4939   },
4940   {
4941     Xandroid_1_w,                       FALSE,  FALSE,
4942     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
4943   },
4944   {
4945     Xandroid_2_w,                       FALSE,  FALSE,
4946     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
4947   },
4948   {
4949     Xandroid_1_s,                       FALSE,  FALSE,
4950     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
4951   },
4952   {
4953     Xandroid_2_s,                       FALSE,  FALSE,
4954     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
4955   },
4956   {
4957     Yandroid_n,                         FALSE,  FALSE,
4958     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
4959   },
4960   {
4961     Yandroid_nB,                        FALSE,  TRUE,
4962     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
4963   },
4964   {
4965     Yandroid_ne,                        FALSE,  FALSE,
4966     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
4967   },
4968   {
4969     Yandroid_neB,                       FALSE,  TRUE,
4970     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
4971   },
4972   {
4973     Yandroid_e,                         FALSE,  FALSE,
4974     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
4975   },
4976   {
4977     Yandroid_eB,                        FALSE,  TRUE,
4978     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
4979   },
4980   {
4981     Yandroid_se,                        FALSE,  FALSE,
4982     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
4983   },
4984   {
4985     Yandroid_seB,                       FALSE,  TRUE,
4986     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4987   },
4988   {
4989     Yandroid_s,                         FALSE,  FALSE,
4990     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
4991   },
4992   {
4993     Yandroid_sB,                        FALSE,  TRUE,
4994     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
4995   },
4996   {
4997     Yandroid_sw,                        FALSE,  FALSE,
4998     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
4999   },
5000   {
5001     Yandroid_swB,                       FALSE,  TRUE,
5002     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
5003   },
5004   {
5005     Yandroid_w,                         FALSE,  FALSE,
5006     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5007   },
5008   {
5009     Yandroid_wB,                        FALSE,  TRUE,
5010     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5011   },
5012   {
5013     Yandroid_nw,                        FALSE,  FALSE,
5014     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
5015   },
5016   {
5017     Yandroid_nwB,                       FALSE,  TRUE,
5018     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
5019   },
5020   {
5021     Xspring,                            TRUE,   FALSE,
5022     EL_SPRING,                          -1, -1
5023   },
5024   {
5025     Xspring_pause,                      FALSE,  FALSE,
5026     EL_SPRING,                          -1, -1
5027   },
5028   {
5029     Xspring_e,                          FALSE,  FALSE,
5030     EL_SPRING,                          -1, -1
5031   },
5032   {
5033     Xspring_w,                          FALSE,  FALSE,
5034     EL_SPRING,                          -1, -1
5035   },
5036   {
5037     Xspring_fall,                       FALSE,  FALSE,
5038     EL_SPRING,                          -1, -1
5039   },
5040   {
5041     Yspring_s,                          FALSE,  FALSE,
5042     EL_SPRING,                          ACTION_FALLING, -1
5043   },
5044   {
5045     Yspring_sB,                         FALSE,  TRUE,
5046     EL_SPRING,                          ACTION_FALLING, -1
5047   },
5048   {
5049     Yspring_e,                          FALSE,  FALSE,
5050     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5051   },
5052   {
5053     Yspring_eB,                         FALSE,  TRUE,
5054     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5055   },
5056   {
5057     Yspring_w,                          FALSE,  FALSE,
5058     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5059   },
5060   {
5061     Yspring_wB,                         FALSE,  TRUE,
5062     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5063   },
5064   {
5065     Yspring_kill_e,                     FALSE,  FALSE,
5066     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5067   },
5068   {
5069     Yspring_kill_eB,                    FALSE,  TRUE,
5070     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5071   },
5072   {
5073     Yspring_kill_w,                     FALSE,  FALSE,
5074     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5075   },
5076   {
5077     Yspring_kill_wB,                    FALSE,  TRUE,
5078     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5079   },
5080   {
5081     Xeater_n,                           TRUE,   FALSE,
5082     EL_YAMYAM_UP,                       -1, -1
5083   },
5084   {
5085     Xeater_e,                           TRUE,   FALSE,
5086     EL_YAMYAM_RIGHT,                    -1, -1
5087   },
5088   {
5089     Xeater_w,                           TRUE,   FALSE,
5090     EL_YAMYAM_LEFT,                     -1, -1
5091   },
5092   {
5093     Xeater_s,                           TRUE,   FALSE,
5094     EL_YAMYAM_DOWN,                     -1, -1
5095   },
5096   {
5097     Yeater_n,                           FALSE,  FALSE,
5098     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5099   },
5100   {
5101     Yeater_nB,                          FALSE,  TRUE,
5102     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5103   },
5104   {
5105     Yeater_e,                           FALSE,  FALSE,
5106     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5107   },
5108   {
5109     Yeater_eB,                          FALSE,  TRUE,
5110     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5111   },
5112   {
5113     Yeater_s,                           FALSE,  FALSE,
5114     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5115   },
5116   {
5117     Yeater_sB,                          FALSE,  TRUE,
5118     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5119   },
5120   {
5121     Yeater_w,                           FALSE,  FALSE,
5122     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5123   },
5124   {
5125     Yeater_wB,                          FALSE,  TRUE,
5126     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5127   },
5128   {
5129     Yeater_stone,                       FALSE,  FALSE,
5130     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
5131   },
5132   {
5133     Yeater_spring,                      FALSE,  FALSE,
5134     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
5135   },
5136   {
5137     Xalien,                             TRUE,   FALSE,
5138     EL_ROBOT,                           -1, -1
5139   },
5140   {
5141     Xalien_pause,                       FALSE,  FALSE,
5142     EL_ROBOT,                           -1, -1
5143   },
5144   {
5145     Yalien_n,                           FALSE,  FALSE,
5146     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5147   },
5148   {
5149     Yalien_nB,                          FALSE,  TRUE,
5150     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5151   },
5152   {
5153     Yalien_e,                           FALSE,  FALSE,
5154     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5155   },
5156   {
5157     Yalien_eB,                          FALSE,  TRUE,
5158     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5159   },
5160   {
5161     Yalien_s,                           FALSE,  FALSE,
5162     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5163   },
5164   {
5165     Yalien_sB,                          FALSE,  TRUE,
5166     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5167   },
5168   {
5169     Yalien_w,                           FALSE,  FALSE,
5170     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5171   },
5172   {
5173     Yalien_wB,                          FALSE,  TRUE,
5174     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5175   },
5176   {
5177     Yalien_stone,                       FALSE,  FALSE,
5178     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
5179   },
5180   {
5181     Yalien_spring,                      FALSE,  FALSE,
5182     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
5183   },
5184   {
5185     Xemerald,                           TRUE,   FALSE,
5186     EL_EMERALD,                         -1, -1
5187   },
5188   {
5189     Xemerald_pause,                     FALSE,  FALSE,
5190     EL_EMERALD,                         -1, -1
5191   },
5192   {
5193     Xemerald_fall,                      FALSE,  FALSE,
5194     EL_EMERALD,                         -1, -1
5195   },
5196   {
5197     Xemerald_shine,                     FALSE,  FALSE,
5198     EL_EMERALD,                         ACTION_TWINKLING, -1
5199   },
5200   {
5201     Yemerald_s,                         FALSE,  FALSE,
5202     EL_EMERALD,                         ACTION_FALLING, -1
5203   },
5204   {
5205     Yemerald_sB,                        FALSE,  TRUE,
5206     EL_EMERALD,                         ACTION_FALLING, -1
5207   },
5208   {
5209     Yemerald_e,                         FALSE,  FALSE,
5210     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5211   },
5212   {
5213     Yemerald_eB,                        FALSE,  TRUE,
5214     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5215   },
5216   {
5217     Yemerald_w,                         FALSE,  FALSE,
5218     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5219   },
5220   {
5221     Yemerald_wB,                        FALSE,  TRUE,
5222     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5223   },
5224   {
5225     Yemerald_eat,                       FALSE,  FALSE,
5226     EL_EMERALD,                         ACTION_COLLECTING, -1
5227   },
5228   {
5229     Yemerald_stone,                     FALSE,  FALSE,
5230     EL_NUT,                             ACTION_BREAKING, -1
5231   },
5232   {
5233     Xdiamond,                           TRUE,   FALSE,
5234     EL_DIAMOND,                         -1, -1
5235   },
5236   {
5237     Xdiamond_pause,                     FALSE,  FALSE,
5238     EL_DIAMOND,                         -1, -1
5239   },
5240   {
5241     Xdiamond_fall,                      FALSE,  FALSE,
5242     EL_DIAMOND,                         -1, -1
5243   },
5244   {
5245     Xdiamond_shine,                     FALSE,  FALSE,
5246     EL_DIAMOND,                         ACTION_TWINKLING, -1
5247   },
5248   {
5249     Ydiamond_s,                         FALSE,  FALSE,
5250     EL_DIAMOND,                         ACTION_FALLING, -1
5251   },
5252   {
5253     Ydiamond_sB,                        FALSE,  TRUE,
5254     EL_DIAMOND,                         ACTION_FALLING, -1
5255   },
5256   {
5257     Ydiamond_e,                         FALSE,  FALSE,
5258     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5259   },
5260   {
5261     Ydiamond_eB,                        FALSE,  TRUE,
5262     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5263   },
5264   {
5265     Ydiamond_w,                         FALSE,  FALSE,
5266     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5267   },
5268   {
5269     Ydiamond_wB,                        FALSE,  TRUE,
5270     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5271   },
5272   {
5273     Ydiamond_eat,                       FALSE,  FALSE,
5274     EL_DIAMOND,                         ACTION_COLLECTING, -1
5275   },
5276   {
5277     Ydiamond_stone,                     FALSE,  FALSE,
5278     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
5279   },
5280   {
5281     Xdrip_fall,                         TRUE,   FALSE,
5282     EL_AMOEBA_DROP,                     -1, -1
5283   },
5284   {
5285     Xdrip_stretch,                      FALSE,  FALSE,
5286     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5287   },
5288   {
5289     Xdrip_stretchB,                     FALSE,  TRUE,
5290     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5291   },
5292   {
5293     Xdrip_eat,                          FALSE,  FALSE,
5294     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
5295   },
5296   {
5297     Ydrip_s1,                           FALSE,  FALSE,
5298     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5299   },
5300   {
5301     Ydrip_s1B,                          FALSE,  TRUE,
5302     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5303   },
5304   {
5305     Ydrip_s2,                           FALSE,  FALSE,
5306     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5307   },
5308   {
5309     Ydrip_s2B,                          FALSE,  TRUE,
5310     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5311   },
5312   {
5313     Xbomb,                              TRUE,   FALSE,
5314     EL_BOMB,                            -1, -1
5315   },
5316   {
5317     Xbomb_pause,                        FALSE,  FALSE,
5318     EL_BOMB,                            -1, -1
5319   },
5320   {
5321     Xbomb_fall,                         FALSE,  FALSE,
5322     EL_BOMB,                            -1, -1
5323   },
5324   {
5325     Ybomb_s,                            FALSE,  FALSE,
5326     EL_BOMB,                            ACTION_FALLING, -1
5327   },
5328   {
5329     Ybomb_sB,                           FALSE,  TRUE,
5330     EL_BOMB,                            ACTION_FALLING, -1
5331   },
5332   {
5333     Ybomb_e,                            FALSE,  FALSE,
5334     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5335   },
5336   {
5337     Ybomb_eB,                           FALSE,  TRUE,
5338     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5339   },
5340   {
5341     Ybomb_w,                            FALSE,  FALSE,
5342     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5343   },
5344   {
5345     Ybomb_wB,                           FALSE,  TRUE,
5346     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5347   },
5348   {
5349     Ybomb_eat,                          FALSE,  FALSE,
5350     EL_BOMB,                            ACTION_ACTIVATING, -1
5351   },
5352   {
5353     Xballoon,                           TRUE,   FALSE,
5354     EL_BALLOON,                         -1, -1
5355   },
5356   {
5357     Yballoon_n,                         FALSE,  FALSE,
5358     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5359   },
5360   {
5361     Yballoon_nB,                        FALSE,  TRUE,
5362     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5363   },
5364   {
5365     Yballoon_e,                         FALSE,  FALSE,
5366     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5367   },
5368   {
5369     Yballoon_eB,                        FALSE,  TRUE,
5370     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5371   },
5372   {
5373     Yballoon_s,                         FALSE,  FALSE,
5374     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5375   },
5376   {
5377     Yballoon_sB,                        FALSE,  TRUE,
5378     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5379   },
5380   {
5381     Yballoon_w,                         FALSE,  FALSE,
5382     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5383   },
5384   {
5385     Yballoon_wB,                        FALSE,  TRUE,
5386     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5387   },
5388   {
5389     Xgrass,                             TRUE,   FALSE,
5390     EL_EMC_GRASS,                       -1, -1
5391   },
5392   {
5393     Ygrass_nB,                          FALSE,  FALSE,
5394     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
5395   },
5396   {
5397     Ygrass_eB,                          FALSE,  FALSE,
5398     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
5399   },
5400   {
5401     Ygrass_sB,                          FALSE,  FALSE,
5402     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
5403   },
5404   {
5405     Ygrass_wB,                          FALSE,  FALSE,
5406     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
5407   },
5408   {
5409     Xdirt,                              TRUE,   FALSE,
5410     EL_SAND,                            -1, -1
5411   },
5412   {
5413     Ydirt_nB,                           FALSE,  FALSE,
5414     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
5415   },
5416   {
5417     Ydirt_eB,                           FALSE,  FALSE,
5418     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
5419   },
5420   {
5421     Ydirt_sB,                           FALSE,  FALSE,
5422     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
5423   },
5424   {
5425     Ydirt_wB,                           FALSE,  FALSE,
5426     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
5427   },
5428   {
5429     Xacid_ne,                           TRUE,   FALSE,
5430     EL_ACID_POOL_TOPRIGHT,              -1, -1
5431   },
5432   {
5433     Xacid_se,                           TRUE,   FALSE,
5434     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
5435   },
5436   {
5437     Xacid_s,                            TRUE,   FALSE,
5438     EL_ACID_POOL_BOTTOM,                -1, -1
5439   },
5440   {
5441     Xacid_sw,                           TRUE,   FALSE,
5442     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
5443   },
5444   {
5445     Xacid_nw,                           TRUE,   FALSE,
5446     EL_ACID_POOL_TOPLEFT,               -1, -1
5447   },
5448   {
5449     Xacid_1,                            TRUE,   FALSE,
5450     EL_ACID,                            -1, -1
5451   },
5452   {
5453     Xacid_2,                            FALSE,  FALSE,
5454     EL_ACID,                            -1, -1
5455   },
5456   {
5457     Xacid_3,                            FALSE,  FALSE,
5458     EL_ACID,                            -1, -1
5459   },
5460   {
5461     Xacid_4,                            FALSE,  FALSE,
5462     EL_ACID,                            -1, -1
5463   },
5464   {
5465     Xacid_5,                            FALSE,  FALSE,
5466     EL_ACID,                            -1, -1
5467   },
5468   {
5469     Xacid_6,                            FALSE,  FALSE,
5470     EL_ACID,                            -1, -1
5471   },
5472   {
5473     Xacid_7,                            FALSE,  FALSE,
5474     EL_ACID,                            -1, -1
5475   },
5476   {
5477     Xacid_8,                            FALSE,  FALSE,
5478     EL_ACID,                            -1, -1
5479   },
5480   {
5481     Xball_1,                            TRUE,   FALSE,
5482     EL_EMC_MAGIC_BALL,                  -1, -1
5483   },
5484   {
5485     Xball_1B,                           FALSE,  FALSE,
5486     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5487   },
5488   {
5489     Xball_2,                            FALSE,  FALSE,
5490     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5491   },
5492   {
5493     Xball_2B,                           FALSE,  FALSE,
5494     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5495   },
5496   {
5497     Yball_eat,                          FALSE,  FALSE,
5498     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
5499   },
5500   {
5501     Ykey_1_eat,                         FALSE,  FALSE,
5502     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
5503   },
5504   {
5505     Ykey_2_eat,                         FALSE,  FALSE,
5506     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
5507   },
5508   {
5509     Ykey_3_eat,                         FALSE,  FALSE,
5510     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
5511   },
5512   {
5513     Ykey_4_eat,                         FALSE,  FALSE,
5514     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
5515   },
5516   {
5517     Ykey_5_eat,                         FALSE,  FALSE,
5518     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
5519   },
5520   {
5521     Ykey_6_eat,                         FALSE,  FALSE,
5522     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
5523   },
5524   {
5525     Ykey_7_eat,                         FALSE,  FALSE,
5526     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
5527   },
5528   {
5529     Ykey_8_eat,                         FALSE,  FALSE,
5530     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
5531   },
5532   {
5533     Ylenses_eat,                        FALSE,  FALSE,
5534     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
5535   },
5536   {
5537     Ymagnify_eat,                       FALSE,  FALSE,
5538     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
5539   },
5540   {
5541     Ygrass_eat,                         FALSE,  FALSE,
5542     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
5543   },
5544   {
5545     Ydirt_eat,                          FALSE,  FALSE,
5546     EL_SAND,                            ACTION_SNAPPING, -1
5547   },
5548   {
5549     Xgrow_ns,                           TRUE,   FALSE,
5550     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
5551   },
5552   {
5553     Ygrow_ns_eat,                       FALSE,  FALSE,
5554     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
5555   },
5556   {
5557     Xgrow_ew,                           TRUE,   FALSE,
5558     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
5559   },
5560   {
5561     Ygrow_ew_eat,                       FALSE,  FALSE,
5562     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
5563   },
5564   {
5565     Xwonderwall,                        TRUE,   FALSE,
5566     EL_MAGIC_WALL,                      -1, -1
5567   },
5568   {
5569     XwonderwallB,                       FALSE,  FALSE,
5570     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
5571   },
5572   {
5573     Xamoeba_1,                          TRUE,   FALSE,
5574     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5575   },
5576   {
5577     Xamoeba_2,                          FALSE,  FALSE,
5578     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5579   },
5580   {
5581     Xamoeba_3,                          FALSE,  FALSE,
5582     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5583   },
5584   {
5585     Xamoeba_4,                          FALSE,  FALSE,
5586     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5587   },
5588   {
5589     Xamoeba_5,                          TRUE,   FALSE,
5590     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5591   },
5592   {
5593     Xamoeba_6,                          FALSE,  FALSE,
5594     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5595   },
5596   {
5597     Xamoeba_7,                          FALSE,  FALSE,
5598     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5599   },
5600   {
5601     Xamoeba_8,                          FALSE,  FALSE,
5602     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5603   },
5604   {
5605     Xdoor_1,                            TRUE,   FALSE,
5606     EL_EM_GATE_1,                       -1, -1
5607   },
5608   {
5609     Xdoor_2,                            TRUE,   FALSE,
5610     EL_EM_GATE_2,                       -1, -1
5611   },
5612   {
5613     Xdoor_3,                            TRUE,   FALSE,
5614     EL_EM_GATE_3,                       -1, -1
5615   },
5616   {
5617     Xdoor_4,                            TRUE,   FALSE,
5618     EL_EM_GATE_4,                       -1, -1
5619   },
5620   {
5621     Xdoor_5,                            TRUE,   FALSE,
5622     EL_EMC_GATE_5,                      -1, -1
5623   },
5624   {
5625     Xdoor_6,                            TRUE,   FALSE,
5626     EL_EMC_GATE_6,                      -1, -1
5627   },
5628   {
5629     Xdoor_7,                            TRUE,   FALSE,
5630     EL_EMC_GATE_7,                      -1, -1
5631   },
5632   {
5633     Xdoor_8,                            TRUE,   FALSE,
5634     EL_EMC_GATE_8,                      -1, -1
5635   },
5636   {
5637     Xkey_1,                             TRUE,   FALSE,
5638     EL_EM_KEY_1,                        -1, -1
5639   },
5640   {
5641     Xkey_2,                             TRUE,   FALSE,
5642     EL_EM_KEY_2,                        -1, -1
5643   },
5644   {
5645     Xkey_3,                             TRUE,   FALSE,
5646     EL_EM_KEY_3,                        -1, -1
5647   },
5648   {
5649     Xkey_4,                             TRUE,   FALSE,
5650     EL_EM_KEY_4,                        -1, -1
5651   },
5652   {
5653     Xkey_5,                             TRUE,   FALSE,
5654     EL_EMC_KEY_5,                       -1, -1
5655   },
5656   {
5657     Xkey_6,                             TRUE,   FALSE,
5658     EL_EMC_KEY_6,                       -1, -1
5659   },
5660   {
5661     Xkey_7,                             TRUE,   FALSE,
5662     EL_EMC_KEY_7,                       -1, -1
5663   },
5664   {
5665     Xkey_8,                             TRUE,   FALSE,
5666     EL_EMC_KEY_8,                       -1, -1
5667   },
5668   {
5669     Xwind_n,                            TRUE,   FALSE,
5670     EL_BALLOON_SWITCH_UP,               -1, -1
5671   },
5672   {
5673     Xwind_e,                            TRUE,   FALSE,
5674     EL_BALLOON_SWITCH_RIGHT,            -1, -1
5675   },
5676   {
5677     Xwind_s,                            TRUE,   FALSE,
5678     EL_BALLOON_SWITCH_DOWN,             -1, -1
5679   },
5680   {
5681     Xwind_w,                            TRUE,   FALSE,
5682     EL_BALLOON_SWITCH_LEFT,             -1, -1
5683   },
5684   {
5685     Xwind_nesw,                         TRUE,   FALSE,
5686     EL_BALLOON_SWITCH_ANY,              -1, -1
5687   },
5688   {
5689     Xwind_stop,                         TRUE,   FALSE,
5690     EL_BALLOON_SWITCH_NONE,             -1, -1
5691   },
5692   {
5693     Xexit,                              TRUE,   FALSE,
5694     EL_EM_EXIT_CLOSED,                  -1, -1
5695   },
5696   {
5697     Xexit_1,                            TRUE,   FALSE,
5698     EL_EM_EXIT_OPEN,                    -1, -1
5699   },
5700   {
5701     Xexit_2,                            FALSE,  FALSE,
5702     EL_EM_EXIT_OPEN,                    -1, -1
5703   },
5704   {
5705     Xexit_3,                            FALSE,  FALSE,
5706     EL_EM_EXIT_OPEN,                    -1, -1
5707   },
5708   {
5709     Xdynamite,                          TRUE,   FALSE,
5710     EL_EM_DYNAMITE,                     -1, -1
5711   },
5712   {
5713     Ydynamite_eat,                      FALSE,  FALSE,
5714     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
5715   },
5716   {
5717     Xdynamite_1,                        TRUE,   FALSE,
5718     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5719   },
5720   {
5721     Xdynamite_2,                        FALSE,  FALSE,
5722     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5723   },
5724   {
5725     Xdynamite_3,                        FALSE,  FALSE,
5726     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5727   },
5728   {
5729     Xdynamite_4,                        FALSE,  FALSE,
5730     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5731   },
5732   {
5733     Xbumper,                            TRUE,   FALSE,
5734     EL_EMC_SPRING_BUMPER,               -1, -1
5735   },
5736   {
5737     XbumperB,                           FALSE,  FALSE,
5738     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
5739   },
5740   {
5741     Xwheel,                             TRUE,   FALSE,
5742     EL_ROBOT_WHEEL,                     -1, -1
5743   },
5744   {
5745     XwheelB,                            FALSE,  FALSE,
5746     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
5747   },
5748   {
5749     Xswitch,                            TRUE,   FALSE,
5750     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
5751   },
5752   {
5753     XswitchB,                           FALSE,  FALSE,
5754     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
5755   },
5756   {
5757     Xsand,                              TRUE,   FALSE,
5758     EL_QUICKSAND_EMPTY,                 -1, -1
5759   },
5760   {
5761     Xsand_stone,                        TRUE,   FALSE,
5762     EL_QUICKSAND_FULL,                  -1, -1
5763   },
5764   {
5765     Xsand_stonein_1,                    FALSE,  TRUE,
5766     EL_ROCK,                            ACTION_FILLING, -1
5767   },
5768   {
5769     Xsand_stonein_2,                    FALSE,  TRUE,
5770     EL_ROCK,                            ACTION_FILLING, -1
5771   },
5772   {
5773     Xsand_stonein_3,                    FALSE,  TRUE,
5774     EL_ROCK,                            ACTION_FILLING, -1
5775   },
5776   {
5777     Xsand_stonein_4,                    FALSE,  TRUE,
5778     EL_ROCK,                            ACTION_FILLING, -1
5779   },
5780   {
5781     Xsand_stonesand_1,                  FALSE,  FALSE,
5782     EL_QUICKSAND_EMPTYING,              -1, -1
5783   },
5784   {
5785     Xsand_stonesand_2,                  FALSE,  FALSE,
5786     EL_QUICKSAND_EMPTYING,              -1, -1
5787   },
5788   {
5789     Xsand_stonesand_3,                  FALSE,  FALSE,
5790     EL_QUICKSAND_EMPTYING,              -1, -1
5791   },
5792   {
5793     Xsand_stonesand_4,                  FALSE,  FALSE,
5794     EL_QUICKSAND_EMPTYING,              -1, -1
5795   },
5796   {
5797     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
5798     EL_QUICKSAND_EMPTYING,              -1, -1
5799   },
5800   {
5801     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
5802     EL_QUICKSAND_EMPTYING,              -1, -1
5803   },
5804   {
5805     Xsand_stoneout_1,                   FALSE,  FALSE,
5806     EL_ROCK,                            ACTION_EMPTYING, -1
5807   },
5808   {
5809     Xsand_stoneout_2,                   FALSE,  FALSE,
5810     EL_ROCK,                            ACTION_EMPTYING, -1
5811   },
5812   {
5813     Xsand_sandstone_1,                  FALSE,  FALSE,
5814     EL_QUICKSAND_FILLING,               -1, -1
5815   },
5816   {
5817     Xsand_sandstone_2,                  FALSE,  FALSE,
5818     EL_QUICKSAND_FILLING,               -1, -1
5819   },
5820   {
5821     Xsand_sandstone_3,                  FALSE,  FALSE,
5822     EL_QUICKSAND_FILLING,               -1, -1
5823   },
5824   {
5825     Xsand_sandstone_4,                  FALSE,  FALSE,
5826     EL_QUICKSAND_FILLING,               -1, -1
5827   },
5828   {
5829     Xplant,                             TRUE,   FALSE,
5830     EL_EMC_PLANT,                       -1, -1
5831   },
5832   {
5833     Yplant,                             FALSE,  FALSE,
5834     EL_EMC_PLANT,                       -1, -1
5835   },
5836   {
5837     Xlenses,                            TRUE,   FALSE,
5838     EL_EMC_LENSES,                      -1, -1
5839   },
5840   {
5841     Xmagnify,                           TRUE,   FALSE,
5842     EL_EMC_MAGNIFIER,                   -1, -1
5843   },
5844   {
5845     Xdripper,                           TRUE,   FALSE,
5846     EL_EMC_DRIPPER,                     -1, -1
5847   },
5848   {
5849     XdripperB,                          FALSE,  FALSE,
5850     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
5851   },
5852   {
5853     Xfake_blank,                        TRUE,   FALSE,
5854     EL_INVISIBLE_WALL,                  -1, -1
5855   },
5856   {
5857     Xfake_blankB,                       FALSE,  FALSE,
5858     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
5859   },
5860   {
5861     Xfake_grass,                        TRUE,   FALSE,
5862     EL_EMC_FAKE_GRASS,                  -1, -1
5863   },
5864   {
5865     Xfake_grassB,                       FALSE,  FALSE,
5866     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
5867   },
5868   {
5869     Xfake_door_1,                       TRUE,   FALSE,
5870     EL_EM_GATE_1_GRAY,                  -1, -1
5871   },
5872   {
5873     Xfake_door_2,                       TRUE,   FALSE,
5874     EL_EM_GATE_2_GRAY,                  -1, -1
5875   },
5876   {
5877     Xfake_door_3,                       TRUE,   FALSE,
5878     EL_EM_GATE_3_GRAY,                  -1, -1
5879   },
5880   {
5881     Xfake_door_4,                       TRUE,   FALSE,
5882     EL_EM_GATE_4_GRAY,                  -1, -1
5883   },
5884   {
5885     Xfake_door_5,                       TRUE,   FALSE,
5886     EL_EMC_GATE_5_GRAY,                 -1, -1
5887   },
5888   {
5889     Xfake_door_6,                       TRUE,   FALSE,
5890     EL_EMC_GATE_6_GRAY,                 -1, -1
5891   },
5892   {
5893     Xfake_door_7,                       TRUE,   FALSE,
5894     EL_EMC_GATE_7_GRAY,                 -1, -1
5895   },
5896   {
5897     Xfake_door_8,                       TRUE,   FALSE,
5898     EL_EMC_GATE_8_GRAY,                 -1, -1
5899   },
5900   {
5901     Xfake_acid_1,                       TRUE,   FALSE,
5902     EL_EMC_FAKE_ACID,                   -1, -1
5903   },
5904   {
5905     Xfake_acid_2,                       FALSE,  FALSE,
5906     EL_EMC_FAKE_ACID,                   -1, -1
5907   },
5908   {
5909     Xfake_acid_3,                       FALSE,  FALSE,
5910     EL_EMC_FAKE_ACID,                   -1, -1
5911   },
5912   {
5913     Xfake_acid_4,                       FALSE,  FALSE,
5914     EL_EMC_FAKE_ACID,                   -1, -1
5915   },
5916   {
5917     Xfake_acid_5,                       FALSE,  FALSE,
5918     EL_EMC_FAKE_ACID,                   -1, -1
5919   },
5920   {
5921     Xfake_acid_6,                       FALSE,  FALSE,
5922     EL_EMC_FAKE_ACID,                   -1, -1
5923   },
5924   {
5925     Xfake_acid_7,                       FALSE,  FALSE,
5926     EL_EMC_FAKE_ACID,                   -1, -1
5927   },
5928   {
5929     Xfake_acid_8,                       FALSE,  FALSE,
5930     EL_EMC_FAKE_ACID,                   -1, -1
5931   },
5932   {
5933     Xsteel_1,                           TRUE,   FALSE,
5934     EL_STEELWALL,                       -1, -1
5935   },
5936   {
5937     Xsteel_2,                           TRUE,   FALSE,
5938     EL_EMC_STEELWALL_2,                 -1, -1
5939   },
5940   {
5941     Xsteel_3,                           TRUE,   FALSE,
5942     EL_EMC_STEELWALL_3,                 -1, -1
5943   },
5944   {
5945     Xsteel_4,                           TRUE,   FALSE,
5946     EL_EMC_STEELWALL_4,                 -1, -1
5947   },
5948   {
5949     Xwall_1,                            TRUE,   FALSE,
5950     EL_WALL,                            -1, -1
5951   },
5952   {
5953     Xwall_2,                            TRUE,   FALSE,
5954     EL_EMC_WALL_14,                     -1, -1
5955   },
5956   {
5957     Xwall_3,                            TRUE,   FALSE,
5958     EL_EMC_WALL_15,                     -1, -1
5959   },
5960   {
5961     Xwall_4,                            TRUE,   FALSE,
5962     EL_EMC_WALL_16,                     -1, -1
5963   },
5964   {
5965     Xround_wall_1,                      TRUE,   FALSE,
5966     EL_WALL_SLIPPERY,                   -1, -1
5967   },
5968   {
5969     Xround_wall_2,                      TRUE,   FALSE,
5970     EL_EMC_WALL_SLIPPERY_2,             -1, -1
5971   },
5972   {
5973     Xround_wall_3,                      TRUE,   FALSE,
5974     EL_EMC_WALL_SLIPPERY_3,             -1, -1
5975   },
5976   {
5977     Xround_wall_4,                      TRUE,   FALSE,
5978     EL_EMC_WALL_SLIPPERY_4,             -1, -1
5979   },
5980   {
5981     Xdecor_1,                           TRUE,   FALSE,
5982     EL_EMC_WALL_8,                      -1, -1
5983   },
5984   {
5985     Xdecor_2,                           TRUE,   FALSE,
5986     EL_EMC_WALL_6,                      -1, -1
5987   },
5988   {
5989     Xdecor_3,                           TRUE,   FALSE,
5990     EL_EMC_WALL_4,                      -1, -1
5991   },
5992   {
5993     Xdecor_4,                           TRUE,   FALSE,
5994     EL_EMC_WALL_7,                      -1, -1
5995   },
5996   {
5997     Xdecor_5,                           TRUE,   FALSE,
5998     EL_EMC_WALL_5,                      -1, -1
5999   },
6000   {
6001     Xdecor_6,                           TRUE,   FALSE,
6002     EL_EMC_WALL_9,                      -1, -1
6003   },
6004   {
6005     Xdecor_7,                           TRUE,   FALSE,
6006     EL_EMC_WALL_10,                     -1, -1
6007   },
6008   {
6009     Xdecor_8,                           TRUE,   FALSE,
6010     EL_EMC_WALL_1,                      -1, -1
6011   },
6012   {
6013     Xdecor_9,                           TRUE,   FALSE,
6014     EL_EMC_WALL_2,                      -1, -1
6015   },
6016   {
6017     Xdecor_10,                          TRUE,   FALSE,
6018     EL_EMC_WALL_3,                      -1, -1
6019   },
6020   {
6021     Xdecor_11,                          TRUE,   FALSE,
6022     EL_EMC_WALL_11,                     -1, -1
6023   },
6024   {
6025     Xdecor_12,                          TRUE,   FALSE,
6026     EL_EMC_WALL_12,                     -1, -1
6027   },
6028   {
6029     Xalpha_0,                           TRUE,   FALSE,
6030     EL_CHAR('0'),                       -1, -1
6031   },
6032   {
6033     Xalpha_1,                           TRUE,   FALSE,
6034     EL_CHAR('1'),                       -1, -1
6035   },
6036   {
6037     Xalpha_2,                           TRUE,   FALSE,
6038     EL_CHAR('2'),                       -1, -1
6039   },
6040   {
6041     Xalpha_3,                           TRUE,   FALSE,
6042     EL_CHAR('3'),                       -1, -1
6043   },
6044   {
6045     Xalpha_4,                           TRUE,   FALSE,
6046     EL_CHAR('4'),                       -1, -1
6047   },
6048   {
6049     Xalpha_5,                           TRUE,   FALSE,
6050     EL_CHAR('5'),                       -1, -1
6051   },
6052   {
6053     Xalpha_6,                           TRUE,   FALSE,
6054     EL_CHAR('6'),                       -1, -1
6055   },
6056   {
6057     Xalpha_7,                           TRUE,   FALSE,
6058     EL_CHAR('7'),                       -1, -1
6059   },
6060   {
6061     Xalpha_8,                           TRUE,   FALSE,
6062     EL_CHAR('8'),                       -1, -1
6063   },
6064   {
6065     Xalpha_9,                           TRUE,   FALSE,
6066     EL_CHAR('9'),                       -1, -1
6067   },
6068   {
6069     Xalpha_excla,                       TRUE,   FALSE,
6070     EL_CHAR('!'),                       -1, -1
6071   },
6072   {
6073     Xalpha_quote,                       TRUE,   FALSE,
6074     EL_CHAR('"'),                       -1, -1
6075   },
6076   {
6077     Xalpha_comma,                       TRUE,   FALSE,
6078     EL_CHAR(','),                       -1, -1
6079   },
6080   {
6081     Xalpha_minus,                       TRUE,   FALSE,
6082     EL_CHAR('-'),                       -1, -1
6083   },
6084   {
6085     Xalpha_perio,                       TRUE,   FALSE,
6086     EL_CHAR('.'),                       -1, -1
6087   },
6088   {
6089     Xalpha_colon,                       TRUE,   FALSE,
6090     EL_CHAR(':'),                       -1, -1
6091   },
6092   {
6093     Xalpha_quest,                       TRUE,   FALSE,
6094     EL_CHAR('?'),                       -1, -1
6095   },
6096   {
6097     Xalpha_a,                           TRUE,   FALSE,
6098     EL_CHAR('A'),                       -1, -1
6099   },
6100   {
6101     Xalpha_b,                           TRUE,   FALSE,
6102     EL_CHAR('B'),                       -1, -1
6103   },
6104   {
6105     Xalpha_c,                           TRUE,   FALSE,
6106     EL_CHAR('C'),                       -1, -1
6107   },
6108   {
6109     Xalpha_d,                           TRUE,   FALSE,
6110     EL_CHAR('D'),                       -1, -1
6111   },
6112   {
6113     Xalpha_e,                           TRUE,   FALSE,
6114     EL_CHAR('E'),                       -1, -1
6115   },
6116   {
6117     Xalpha_f,                           TRUE,   FALSE,
6118     EL_CHAR('F'),                       -1, -1
6119   },
6120   {
6121     Xalpha_g,                           TRUE,   FALSE,
6122     EL_CHAR('G'),                       -1, -1
6123   },
6124   {
6125     Xalpha_h,                           TRUE,   FALSE,
6126     EL_CHAR('H'),                       -1, -1
6127   },
6128   {
6129     Xalpha_i,                           TRUE,   FALSE,
6130     EL_CHAR('I'),                       -1, -1
6131   },
6132   {
6133     Xalpha_j,                           TRUE,   FALSE,
6134     EL_CHAR('J'),                       -1, -1
6135   },
6136   {
6137     Xalpha_k,                           TRUE,   FALSE,
6138     EL_CHAR('K'),                       -1, -1
6139   },
6140   {
6141     Xalpha_l,                           TRUE,   FALSE,
6142     EL_CHAR('L'),                       -1, -1
6143   },
6144   {
6145     Xalpha_m,                           TRUE,   FALSE,
6146     EL_CHAR('M'),                       -1, -1
6147   },
6148   {
6149     Xalpha_n,                           TRUE,   FALSE,
6150     EL_CHAR('N'),                       -1, -1
6151   },
6152   {
6153     Xalpha_o,                           TRUE,   FALSE,
6154     EL_CHAR('O'),                       -1, -1
6155   },
6156   {
6157     Xalpha_p,                           TRUE,   FALSE,
6158     EL_CHAR('P'),                       -1, -1
6159   },
6160   {
6161     Xalpha_q,                           TRUE,   FALSE,
6162     EL_CHAR('Q'),                       -1, -1
6163   },
6164   {
6165     Xalpha_r,                           TRUE,   FALSE,
6166     EL_CHAR('R'),                       -1, -1
6167   },
6168   {
6169     Xalpha_s,                           TRUE,   FALSE,
6170     EL_CHAR('S'),                       -1, -1
6171   },
6172   {
6173     Xalpha_t,                           TRUE,   FALSE,
6174     EL_CHAR('T'),                       -1, -1
6175   },
6176   {
6177     Xalpha_u,                           TRUE,   FALSE,
6178     EL_CHAR('U'),                       -1, -1
6179   },
6180   {
6181     Xalpha_v,                           TRUE,   FALSE,
6182     EL_CHAR('V'),                       -1, -1
6183   },
6184   {
6185     Xalpha_w,                           TRUE,   FALSE,
6186     EL_CHAR('W'),                       -1, -1
6187   },
6188   {
6189     Xalpha_x,                           TRUE,   FALSE,
6190     EL_CHAR('X'),                       -1, -1
6191   },
6192   {
6193     Xalpha_y,                           TRUE,   FALSE,
6194     EL_CHAR('Y'),                       -1, -1
6195   },
6196   {
6197     Xalpha_z,                           TRUE,   FALSE,
6198     EL_CHAR('Z'),                       -1, -1
6199   },
6200   {
6201     Xalpha_arrow_e,                     TRUE,   FALSE,
6202     EL_CHAR('>'),                       -1, -1
6203   },
6204   {
6205     Xalpha_arrow_w,                     TRUE,   FALSE,
6206     EL_CHAR('<'),                       -1, -1
6207   },
6208   {
6209     Xalpha_copyr,                       TRUE,   FALSE,
6210     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
6211   },
6212
6213   {
6214     Xboom_bug,                          FALSE,  FALSE,
6215     EL_BUG,                             ACTION_EXPLODING, -1
6216   },
6217   {
6218     Xboom_bomb,                         FALSE,  FALSE,
6219     EL_BOMB,                            ACTION_EXPLODING, -1
6220   },
6221   {
6222     Xboom_android,                      FALSE,  FALSE,
6223     EL_EMC_ANDROID,                     ACTION_OTHER, -1
6224   },
6225   {
6226     Xboom_1,                            FALSE,  FALSE,
6227     EL_DEFAULT,                         ACTION_EXPLODING, -1
6228   },
6229   {
6230     Xboom_2,                            FALSE,  FALSE,
6231     EL_DEFAULT,                         ACTION_EXPLODING, -1
6232   },
6233   {
6234     Znormal,                            FALSE,  FALSE,
6235     EL_EMPTY,                           -1, -1
6236   },
6237   {
6238     Zdynamite,                          FALSE,  FALSE,
6239     EL_EMPTY,                           -1, -1
6240   },
6241   {
6242     Zplayer,                            FALSE,  FALSE,
6243     EL_EMPTY,                           -1, -1
6244   },
6245   {
6246     ZBORDER,                            FALSE,  FALSE,
6247     EL_EMPTY,                           -1, -1
6248   },
6249
6250   {
6251     -1,                                 FALSE,  FALSE,
6252     -1,                                 -1, -1
6253   }
6254 };
6255
6256 static struct Mapping_EM_to_RND_player
6257 {
6258   int action_em;
6259   int player_nr;
6260
6261   int element_rnd;
6262   int action;
6263   int direction;
6264 }
6265 em_player_mapping_list[] =
6266 {
6267   {
6268     SPR_walk + 0,                       0,
6269     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
6270   },
6271   {
6272     SPR_walk + 1,                       0,
6273     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
6274   },
6275   {
6276     SPR_walk + 2,                       0,
6277     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
6278   },
6279   {
6280     SPR_walk + 3,                       0,
6281     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
6282   },
6283   {
6284     SPR_push + 0,                       0,
6285     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
6286   },
6287   {
6288     SPR_push + 1,                       0,
6289     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
6290   },
6291   {
6292     SPR_push + 2,                       0,
6293     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
6294   },
6295   {
6296     SPR_push + 3,                       0,
6297     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
6298   },
6299   {
6300     SPR_spray + 0,                      0,
6301     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
6302   },
6303   {
6304     SPR_spray + 1,                      0,
6305     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6306   },
6307   {
6308     SPR_spray + 2,                      0,
6309     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
6310   },
6311   {
6312     SPR_spray + 3,                      0,
6313     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
6314   },
6315   {
6316     SPR_walk + 0,                       1,
6317     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
6318   },
6319   {
6320     SPR_walk + 1,                       1,
6321     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
6322   },
6323   {
6324     SPR_walk + 2,                       1,
6325     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
6326   },
6327   {
6328     SPR_walk + 3,                       1,
6329     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
6330   },
6331   {
6332     SPR_push + 0,                       1,
6333     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
6334   },
6335   {
6336     SPR_push + 1,                       1,
6337     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
6338   },
6339   {
6340     SPR_push + 2,                       1,
6341     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
6342   },
6343   {
6344     SPR_push + 3,                       1,
6345     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
6346   },
6347   {
6348     SPR_spray + 0,                      1,
6349     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
6350   },
6351   {
6352     SPR_spray + 1,                      1,
6353     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6354   },
6355   {
6356     SPR_spray + 2,                      1,
6357     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
6358   },
6359   {
6360     SPR_spray + 3,                      1,
6361     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
6362   },
6363   {
6364     SPR_still,                          0,
6365     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
6366   },
6367   {
6368     SPR_still,                          1,
6369     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
6370   },
6371   {
6372     SPR_walk + 0,                       2,
6373     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
6374   },
6375   {
6376     SPR_walk + 1,                       2,
6377     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
6378   },
6379   {
6380     SPR_walk + 2,                       2,
6381     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
6382   },
6383   {
6384     SPR_walk + 3,                       2,
6385     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
6386   },
6387   {
6388     SPR_push + 0,                       2,
6389     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
6390   },
6391   {
6392     SPR_push + 1,                       2,
6393     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
6394   },
6395   {
6396     SPR_push + 2,                       2,
6397     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
6398   },
6399   {
6400     SPR_push + 3,                       2,
6401     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
6402   },
6403   {
6404     SPR_spray + 0,                      2,
6405     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
6406   },
6407   {
6408     SPR_spray + 1,                      2,
6409     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6410   },
6411   {
6412     SPR_spray + 2,                      2,
6413     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
6414   },
6415   {
6416     SPR_spray + 3,                      2,
6417     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
6418   },
6419   {
6420     SPR_walk + 0,                       3,
6421     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
6422   },
6423   {
6424     SPR_walk + 1,                       3,
6425     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
6426   },
6427   {
6428     SPR_walk + 2,                       3,
6429     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
6430   },
6431   {
6432     SPR_walk + 3,                       3,
6433     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
6434   },
6435   {
6436     SPR_push + 0,                       3,
6437     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
6438   },
6439   {
6440     SPR_push + 1,                       3,
6441     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
6442   },
6443   {
6444     SPR_push + 2,                       3,
6445     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
6446   },
6447   {
6448     SPR_push + 3,                       3,
6449     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
6450   },
6451   {
6452     SPR_spray + 0,                      3,
6453     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
6454   },
6455   {
6456     SPR_spray + 1,                      3,
6457     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6458   },
6459   {
6460     SPR_spray + 2,                      3,
6461     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
6462   },
6463   {
6464     SPR_spray + 3,                      3,
6465     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
6466   },
6467   {
6468     SPR_still,                          2,
6469     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
6470   },
6471   {
6472     SPR_still,                          3,
6473     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
6474   },
6475
6476   {
6477     -1,                                 -1,
6478     -1,                                 -1, -1
6479   }
6480 };
6481
6482 int map_element_RND_to_EM(int element_rnd)
6483 {
6484   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6485   static boolean mapping_initialized = FALSE;
6486
6487   if (!mapping_initialized)
6488   {
6489     int i;
6490
6491     /* return "Xalpha_quest" for all undefined elements in mapping array */
6492     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6493       mapping_RND_to_EM[i] = Xalpha_quest;
6494
6495     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6496       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6497         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6498           em_object_mapping_list[i].element_em;
6499
6500     mapping_initialized = TRUE;
6501   }
6502
6503   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6504     return mapping_RND_to_EM[element_rnd];
6505
6506   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6507
6508   return EL_UNKNOWN;
6509 }
6510
6511 int map_element_EM_to_RND(int element_em)
6512 {
6513   static unsigned short mapping_EM_to_RND[TILE_MAX];
6514   static boolean mapping_initialized = FALSE;
6515
6516   if (!mapping_initialized)
6517   {
6518     int i;
6519
6520     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6521     for (i = 0; i < TILE_MAX; i++)
6522       mapping_EM_to_RND[i] = EL_UNKNOWN;
6523
6524     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6525       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6526         em_object_mapping_list[i].element_rnd;
6527
6528     mapping_initialized = TRUE;
6529   }
6530
6531   if (element_em >= 0 && element_em < TILE_MAX)
6532     return mapping_EM_to_RND[element_em];
6533
6534   Error(ERR_WARN, "invalid EM level element %d", element_em);
6535
6536   return EL_UNKNOWN;
6537 }
6538
6539 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6540 {
6541   struct LevelInfo_EM *level_em = level->native_em_level;
6542   struct LEVEL *lev = level_em->lev;
6543   int i, j;
6544
6545   for (i = 0; i < TILE_MAX; i++)
6546     lev->android_array[i] = Xblank;
6547
6548   for (i = 0; i < level->num_android_clone_elements; i++)
6549   {
6550     int element_rnd = level->android_clone_element[i];
6551     int element_em = map_element_RND_to_EM(element_rnd);
6552
6553     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6554       if (em_object_mapping_list[j].element_rnd == element_rnd)
6555         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6556   }
6557 }
6558
6559 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6560 {
6561   struct LevelInfo_EM *level_em = level->native_em_level;
6562   struct LEVEL *lev = level_em->lev;
6563   int i, j;
6564
6565   level->num_android_clone_elements = 0;
6566
6567   for (i = 0; i < TILE_MAX; i++)
6568   {
6569     int element_em = lev->android_array[i];
6570     int element_rnd;
6571     boolean element_found = FALSE;
6572
6573     if (element_em == Xblank)
6574       continue;
6575
6576     element_rnd = map_element_EM_to_RND(element_em);
6577
6578     for (j = 0; j < level->num_android_clone_elements; j++)
6579       if (level->android_clone_element[j] == element_rnd)
6580         element_found = TRUE;
6581
6582     if (!element_found)
6583     {
6584       level->android_clone_element[level->num_android_clone_elements++] =
6585         element_rnd;
6586
6587       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6588         break;
6589     }
6590   }
6591
6592   if (level->num_android_clone_elements == 0)
6593   {
6594     level->num_android_clone_elements = 1;
6595     level->android_clone_element[0] = EL_EMPTY;
6596   }
6597 }
6598
6599 int map_direction_RND_to_EM(int direction)
6600 {
6601   return (direction == MV_UP    ? 0 :
6602           direction == MV_RIGHT ? 1 :
6603           direction == MV_DOWN  ? 2 :
6604           direction == MV_LEFT  ? 3 :
6605           -1);
6606 }
6607
6608 int map_direction_EM_to_RND(int direction)
6609 {
6610   return (direction == 0 ? MV_UP    :
6611           direction == 1 ? MV_RIGHT :
6612           direction == 2 ? MV_DOWN  :
6613           direction == 3 ? MV_LEFT  :
6614           MV_NONE);
6615 }
6616
6617 int map_element_RND_to_SP(int element_rnd)
6618 {
6619   int element_sp = 0x20;        /* map unknown elements to yellow "hardware" */
6620
6621   if (element_rnd >= EL_SP_START &&
6622       element_rnd <= EL_SP_END)
6623     element_sp = element_rnd - EL_SP_START;
6624   else if (element_rnd == EL_EMPTY_SPACE)
6625     element_sp = 0x00;
6626   else if (element_rnd == EL_INVISIBLE_WALL)
6627     element_sp = 0x28;
6628
6629   return element_sp;
6630 }
6631
6632 int map_element_SP_to_RND(int element_sp)
6633 {
6634   int element_rnd = EL_UNKNOWN;
6635
6636   if (element_sp >= 0x00 &&
6637       element_sp <= 0x27)
6638     element_rnd = EL_SP_START + element_sp;
6639   else if (element_sp == 0x28)
6640     element_rnd = EL_INVISIBLE_WALL;
6641
6642   return element_rnd;
6643 }
6644
6645 int map_action_SP_to_RND(int action_sp)
6646 {
6647   switch (action_sp)
6648   {
6649     case actActive:             return ACTION_ACTIVE;
6650     case actImpact:             return ACTION_IMPACT;
6651     case actExploding:          return ACTION_EXPLODING;
6652     case actDigging:            return ACTION_DIGGING;
6653     case actSnapping:           return ACTION_SNAPPING;
6654     case actCollecting:         return ACTION_COLLECTING;
6655     case actPassing:            return ACTION_PASSING;
6656     case actPushing:            return ACTION_PUSHING;
6657     case actDropping:           return ACTION_DROPPING;
6658
6659     default:                    return ACTION_DEFAULT;
6660   }
6661 }
6662
6663 int get_next_element(int element)
6664 {
6665   switch (element)
6666   {
6667     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
6668     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
6669     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
6670     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
6671     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
6672     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
6673     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
6674     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
6675     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
6676     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
6677     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
6678
6679     default:                            return element;
6680   }
6681 }
6682
6683 int el_act_dir2img(int element, int action, int direction)
6684 {
6685   element = GFX_ELEMENT(element);
6686   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6687
6688   /* direction_graphic[][] == graphic[] for undefined direction graphics */
6689   return element_info[element].direction_graphic[action][direction];
6690 }
6691
6692 static int el_act_dir2crm(int element, int action, int direction)
6693 {
6694   element = GFX_ELEMENT(element);
6695   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6696
6697   /* direction_graphic[][] == graphic[] for undefined direction graphics */
6698   return element_info[element].direction_crumbled[action][direction];
6699 }
6700
6701 int el_act2img(int element, int action)
6702 {
6703   element = GFX_ELEMENT(element);
6704
6705   return element_info[element].graphic[action];
6706 }
6707
6708 int el_act2crm(int element, int action)
6709 {
6710   element = GFX_ELEMENT(element);
6711
6712   return element_info[element].crumbled[action];
6713 }
6714
6715 int el_dir2img(int element, int direction)
6716 {
6717   element = GFX_ELEMENT(element);
6718
6719   return el_act_dir2img(element, ACTION_DEFAULT, direction);
6720 }
6721
6722 int el2baseimg(int element)
6723 {
6724   return element_info[element].graphic[ACTION_DEFAULT];
6725 }
6726
6727 int el2img(int element)
6728 {
6729   element = GFX_ELEMENT(element);
6730
6731   return element_info[element].graphic[ACTION_DEFAULT];
6732 }
6733
6734 int el2edimg(int element)
6735 {
6736   element = GFX_ELEMENT(element);
6737
6738   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6739 }
6740
6741 int el2preimg(int element)
6742 {
6743   element = GFX_ELEMENT(element);
6744
6745   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6746 }
6747
6748 int el2panelimg(int element)
6749 {
6750   element = GFX_ELEMENT(element);
6751
6752   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6753 }
6754
6755 int font2baseimg(int font_nr)
6756 {
6757   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6758 }
6759
6760 int getBeltNrFromBeltElement(int element)
6761 {
6762   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6763           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6764           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6765 }
6766
6767 int getBeltNrFromBeltActiveElement(int element)
6768 {
6769   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6770           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6771           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6772 }
6773
6774 int getBeltNrFromBeltSwitchElement(int element)
6775 {
6776   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6777           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6778           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6779 }
6780
6781 int getBeltDirNrFromBeltElement(int element)
6782 {
6783   static int belt_base_element[4] =
6784   {
6785     EL_CONVEYOR_BELT_1_LEFT,
6786     EL_CONVEYOR_BELT_2_LEFT,
6787     EL_CONVEYOR_BELT_3_LEFT,
6788     EL_CONVEYOR_BELT_4_LEFT
6789   };
6790
6791   int belt_nr = getBeltNrFromBeltElement(element);
6792   int belt_dir_nr = element - belt_base_element[belt_nr];
6793
6794   return (belt_dir_nr % 3);
6795 }
6796
6797 int getBeltDirNrFromBeltSwitchElement(int element)
6798 {
6799   static int belt_base_element[4] =
6800   {
6801     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6802     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6803     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6804     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6805   };
6806
6807   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6808   int belt_dir_nr = element - belt_base_element[belt_nr];
6809
6810   return (belt_dir_nr % 3);
6811 }
6812
6813 int getBeltDirFromBeltElement(int element)
6814 {
6815   static int belt_move_dir[3] =
6816   {
6817     MV_LEFT,
6818     MV_NONE,
6819     MV_RIGHT
6820   };
6821
6822   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6823
6824   return belt_move_dir[belt_dir_nr];
6825 }
6826
6827 int getBeltDirFromBeltSwitchElement(int element)
6828 {
6829   static int belt_move_dir[3] =
6830   {
6831     MV_LEFT,
6832     MV_NONE,
6833     MV_RIGHT
6834   };
6835
6836   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6837
6838   return belt_move_dir[belt_dir_nr];
6839 }
6840
6841 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6842 {
6843   static int belt_base_element[4] =
6844   {
6845     EL_CONVEYOR_BELT_1_LEFT,
6846     EL_CONVEYOR_BELT_2_LEFT,
6847     EL_CONVEYOR_BELT_3_LEFT,
6848     EL_CONVEYOR_BELT_4_LEFT
6849   };
6850
6851   return belt_base_element[belt_nr] + belt_dir_nr;
6852 }
6853
6854 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6855 {
6856   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6857
6858   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6859 }
6860
6861 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6862 {
6863   static int belt_base_element[4] =
6864   {
6865     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6866     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6867     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6868     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6869   };
6870
6871   return belt_base_element[belt_nr] + belt_dir_nr;
6872 }
6873
6874 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6875 {
6876   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6877
6878   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6879 }
6880
6881 boolean getTeamMode_EM()
6882 {
6883   return game.team_mode;
6884 }
6885
6886 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6887 {
6888   int game_frame_delay_value;
6889
6890   game_frame_delay_value =
6891     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6892      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6893      GameFrameDelay);
6894
6895   if (tape.playing && tape.warp_forward && !tape.pausing)
6896     game_frame_delay_value = 0;
6897
6898   return game_frame_delay_value;
6899 }
6900
6901 unsigned int InitRND(int seed)
6902 {
6903   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6904     return InitEngineRandom_EM(seed);
6905   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6906     return InitEngineRandom_SP(seed);
6907   else
6908     return InitEngineRandom_RND(seed);
6909 }
6910
6911 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6912 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6913
6914 inline static int get_effective_element_EM(int tile, int frame_em)
6915 {
6916   int element             = object_mapping[tile].element_rnd;
6917   int action              = object_mapping[tile].action;
6918   boolean is_backside     = object_mapping[tile].is_backside;
6919   boolean action_removing = (action == ACTION_DIGGING ||
6920                              action == ACTION_SNAPPING ||
6921                              action == ACTION_COLLECTING);
6922
6923   if (frame_em < 7)
6924   {
6925     switch (tile)
6926     {
6927       case Yacid_splash_eB:
6928       case Yacid_splash_wB:
6929         return (frame_em > 5 ? EL_EMPTY : element);
6930
6931       default:
6932         return element;
6933     }
6934   }
6935   else  /* frame_em == 7 */
6936   {
6937     switch (tile)
6938     {
6939       case Yacid_splash_eB:
6940       case Yacid_splash_wB:
6941         return EL_EMPTY;
6942
6943       case Yemerald_stone:
6944         return EL_EMERALD;
6945
6946       case Ydiamond_stone:
6947         return EL_ROCK;
6948
6949       case Xdrip_stretch:
6950       case Xdrip_stretchB:
6951       case Ydrip_s1:
6952       case Ydrip_s1B:
6953       case Xball_1B:
6954       case Xball_2:
6955       case Xball_2B:
6956       case Yball_eat:
6957       case Ykey_1_eat:
6958       case Ykey_2_eat:
6959       case Ykey_3_eat:
6960       case Ykey_4_eat:
6961       case Ykey_5_eat:
6962       case Ykey_6_eat:
6963       case Ykey_7_eat:
6964       case Ykey_8_eat:
6965       case Ylenses_eat:
6966       case Ymagnify_eat:
6967       case Ygrass_eat:
6968       case Ydirt_eat:
6969       case Xsand_stonein_1:
6970       case Xsand_stonein_2:
6971       case Xsand_stonein_3:
6972       case Xsand_stonein_4:
6973         return element;
6974
6975       default:
6976         return (is_backside || action_removing ? EL_EMPTY : element);
6977     }
6978   }
6979 }
6980
6981 inline static boolean check_linear_animation_EM(int tile)
6982 {
6983   switch (tile)
6984   {
6985     case Xsand_stonesand_1:
6986     case Xsand_stonesand_quickout_1:
6987     case Xsand_sandstone_1:
6988     case Xsand_stonein_1:
6989     case Xsand_stoneout_1:
6990     case Xboom_1:
6991     case Xdynamite_1:
6992     case Ybug_w_n:
6993     case Ybug_n_e:
6994     case Ybug_e_s:
6995     case Ybug_s_w:
6996     case Ybug_e_n:
6997     case Ybug_s_e:
6998     case Ybug_w_s:
6999     case Ybug_n_w:
7000     case Ytank_w_n:
7001     case Ytank_n_e:
7002     case Ytank_e_s:
7003     case Ytank_s_w:
7004     case Ytank_e_n:
7005     case Ytank_s_e:
7006     case Ytank_w_s:
7007     case Ytank_n_w:
7008     case Yacid_splash_eB:
7009     case Yacid_splash_wB:
7010     case Yemerald_stone:
7011       return TRUE;
7012   }
7013
7014   return FALSE;
7015 }
7016
7017 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7018                                             boolean has_crumbled_graphics,
7019                                             int crumbled, int sync_frame)
7020 {
7021   /* if element can be crumbled, but certain action graphics are just empty
7022      space (like instantly snapping sand to empty space in 1 frame), do not
7023      treat these empty space graphics as crumbled graphics in EMC engine */
7024   if (crumbled == IMG_EMPTY_SPACE)
7025     has_crumbled_graphics = FALSE;
7026
7027   if (has_crumbled_graphics)
7028   {
7029     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7030     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7031                                            g_crumbled->anim_delay,
7032                                            g_crumbled->anim_mode,
7033                                            g_crumbled->anim_start_frame,
7034                                            sync_frame);
7035
7036     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7037                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7038
7039     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7040
7041     g_em->has_crumbled_graphics = TRUE;
7042   }
7043   else
7044   {
7045     g_em->crumbled_bitmap = NULL;
7046     g_em->crumbled_src_x = 0;
7047     g_em->crumbled_src_y = 0;
7048     g_em->crumbled_border_size = 0;
7049
7050     g_em->has_crumbled_graphics = FALSE;
7051   }
7052 }
7053
7054 void ResetGfxAnimation_EM(int x, int y, int tile)
7055 {
7056   GfxFrame[x][y] = 0;
7057 }
7058
7059 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7060                         int tile, int frame_em, int x, int y)
7061 {
7062   int action = object_mapping[tile].action;
7063   int direction = object_mapping[tile].direction;
7064   int effective_element = get_effective_element_EM(tile, frame_em);
7065   int graphic = (direction == MV_NONE ?
7066                  el_act2img(effective_element, action) :
7067                  el_act_dir2img(effective_element, action, direction));
7068   struct GraphicInfo *g = &graphic_info[graphic];
7069   int sync_frame;
7070   boolean action_removing = (action == ACTION_DIGGING ||
7071                              action == ACTION_SNAPPING ||
7072                              action == ACTION_COLLECTING);
7073   boolean action_moving   = (action == ACTION_FALLING ||
7074                              action == ACTION_MOVING ||
7075                              action == ACTION_PUSHING ||
7076                              action == ACTION_EATING ||
7077                              action == ACTION_FILLING ||
7078                              action == ACTION_EMPTYING);
7079   boolean action_falling  = (action == ACTION_FALLING ||
7080                              action == ACTION_FILLING ||
7081                              action == ACTION_EMPTYING);
7082
7083   /* special case: graphic uses "2nd movement tile" and has defined
7084      7 frames for movement animation (or less) => use default graphic
7085      for last (8th) frame which ends the movement animation */
7086   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7087   {
7088     action = ACTION_DEFAULT;    /* (keep action_* unchanged for now) */
7089     graphic = (direction == MV_NONE ?
7090                el_act2img(effective_element, action) :
7091                el_act_dir2img(effective_element, action, direction));
7092
7093     g = &graphic_info[graphic];
7094   }
7095
7096   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7097   {
7098     GfxFrame[x][y] = 0;
7099   }
7100   else if (action_moving)
7101   {
7102     boolean is_backside = object_mapping[tile].is_backside;
7103
7104     if (is_backside)
7105     {
7106       int direction = object_mapping[tile].direction;
7107       int move_dir = (action_falling ? MV_DOWN : direction);
7108
7109       GfxFrame[x][y]++;
7110
7111 #if 1
7112       /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7113       if (g->double_movement && frame_em == 0)
7114         GfxFrame[x][y] = 0;
7115 #endif
7116
7117       if (move_dir == MV_LEFT)
7118         GfxFrame[x - 1][y] = GfxFrame[x][y];
7119       else if (move_dir == MV_RIGHT)
7120         GfxFrame[x + 1][y] = GfxFrame[x][y];
7121       else if (move_dir == MV_UP)
7122         GfxFrame[x][y - 1] = GfxFrame[x][y];
7123       else if (move_dir == MV_DOWN)
7124         GfxFrame[x][y + 1] = GfxFrame[x][y];
7125     }
7126   }
7127   else
7128   {
7129     GfxFrame[x][y]++;
7130
7131     /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7132     if (tile == Xsand_stonesand_quickout_1 ||
7133         tile == Xsand_stonesand_quickout_2)
7134       GfxFrame[x][y]++;
7135   }
7136
7137   if (graphic_info[graphic].anim_global_sync)
7138     sync_frame = FrameCounter;
7139   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7140     sync_frame = GfxFrame[x][y];
7141   else
7142     sync_frame = 0;     /* playfield border (pseudo steel) */
7143
7144   SetRandomAnimationValue(x, y);
7145
7146   int frame = getAnimationFrame(g->anim_frames,
7147                                 g->anim_delay,
7148                                 g->anim_mode,
7149                                 g->anim_start_frame,
7150                                 sync_frame);
7151
7152   g_em->unique_identifier =
7153     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7154 }
7155
7156 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7157                                   int tile, int frame_em, int x, int y)
7158 {
7159   int action = object_mapping[tile].action;
7160   int direction = object_mapping[tile].direction;
7161   boolean is_backside = object_mapping[tile].is_backside;
7162   int effective_element = get_effective_element_EM(tile, frame_em);
7163   int effective_action = action;
7164   int graphic = (direction == MV_NONE ?
7165                  el_act2img(effective_element, effective_action) :
7166                  el_act_dir2img(effective_element, effective_action,
7167                                 direction));
7168   int crumbled = (direction == MV_NONE ?
7169                   el_act2crm(effective_element, effective_action) :
7170                   el_act_dir2crm(effective_element, effective_action,
7171                                  direction));
7172   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7173   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7174   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7175   struct GraphicInfo *g = &graphic_info[graphic];
7176   int sync_frame;
7177
7178   /* special case: graphic uses "2nd movement tile" and has defined
7179      7 frames for movement animation (or less) => use default graphic
7180      for last (8th) frame which ends the movement animation */
7181   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7182   {
7183     effective_action = ACTION_DEFAULT;
7184     graphic = (direction == MV_NONE ?
7185                el_act2img(effective_element, effective_action) :
7186                el_act_dir2img(effective_element, effective_action,
7187                               direction));
7188     crumbled = (direction == MV_NONE ?
7189                 el_act2crm(effective_element, effective_action) :
7190                 el_act_dir2crm(effective_element, effective_action,
7191                                direction));
7192
7193     g = &graphic_info[graphic];
7194   }
7195
7196   if (graphic_info[graphic].anim_global_sync)
7197     sync_frame = FrameCounter;
7198   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7199     sync_frame = GfxFrame[x][y];
7200   else
7201     sync_frame = 0;     /* playfield border (pseudo steel) */
7202
7203   SetRandomAnimationValue(x, y);
7204
7205   int frame = getAnimationFrame(g->anim_frames,
7206                                 g->anim_delay,
7207                                 g->anim_mode,
7208                                 g->anim_start_frame,
7209                                 sync_frame);
7210
7211   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7212                       g->double_movement && is_backside);
7213
7214   /* (updating the "crumbled" graphic definitions is probably not really needed,
7215      as animations for crumbled graphics can't be longer than one EMC cycle) */
7216   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7217                            sync_frame);
7218 }
7219
7220 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7221                                   int player_nr, int anim, int frame_em)
7222 {
7223   int element   = player_mapping[player_nr][anim].element_rnd;
7224   int action    = player_mapping[player_nr][anim].action;
7225   int direction = player_mapping[player_nr][anim].direction;
7226   int graphic = (direction == MV_NONE ?
7227                  el_act2img(element, action) :
7228                  el_act_dir2img(element, action, direction));
7229   struct GraphicInfo *g = &graphic_info[graphic];
7230   int sync_frame;
7231
7232   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7233
7234   stored_player[player_nr].StepFrame = frame_em;
7235
7236   sync_frame = stored_player[player_nr].Frame;
7237
7238   int frame = getAnimationFrame(g->anim_frames,
7239                                 g->anim_delay,
7240                                 g->anim_mode,
7241                                 g->anim_start_frame,
7242                                 sync_frame);
7243
7244   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7245                       &g_em->src_x, &g_em->src_y, FALSE);
7246 }
7247
7248 void InitGraphicInfo_EM(void)
7249 {
7250   int i, j, p;
7251
7252 #if DEBUG_EM_GFX
7253   int num_em_gfx_errors = 0;
7254
7255   if (graphic_info_em_object[0][0].bitmap == NULL)
7256   {
7257     /* EM graphics not yet initialized in em_open_all() */
7258
7259     return;
7260   }
7261
7262   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7263 #endif
7264
7265   /* always start with reliable default values */
7266   for (i = 0; i < TILE_MAX; i++)
7267   {
7268     object_mapping[i].element_rnd = EL_UNKNOWN;
7269     object_mapping[i].is_backside = FALSE;
7270     object_mapping[i].action = ACTION_DEFAULT;
7271     object_mapping[i].direction = MV_NONE;
7272   }
7273
7274   /* always start with reliable default values */
7275   for (p = 0; p < MAX_PLAYERS; p++)
7276   {
7277     for (i = 0; i < SPR_MAX; i++)
7278     {
7279       player_mapping[p][i].element_rnd = EL_UNKNOWN;
7280       player_mapping[p][i].action = ACTION_DEFAULT;
7281       player_mapping[p][i].direction = MV_NONE;
7282     }
7283   }
7284
7285   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7286   {
7287     int e = em_object_mapping_list[i].element_em;
7288
7289     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7290     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7291
7292     if (em_object_mapping_list[i].action != -1)
7293       object_mapping[e].action = em_object_mapping_list[i].action;
7294
7295     if (em_object_mapping_list[i].direction != -1)
7296       object_mapping[e].direction =
7297         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7298   }
7299
7300   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7301   {
7302     int a = em_player_mapping_list[i].action_em;
7303     int p = em_player_mapping_list[i].player_nr;
7304
7305     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7306
7307     if (em_player_mapping_list[i].action != -1)
7308       player_mapping[p][a].action = em_player_mapping_list[i].action;
7309
7310     if (em_player_mapping_list[i].direction != -1)
7311       player_mapping[p][a].direction =
7312         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7313   }
7314
7315   for (i = 0; i < TILE_MAX; i++)
7316   {
7317     int element = object_mapping[i].element_rnd;
7318     int action = object_mapping[i].action;
7319     int direction = object_mapping[i].direction;
7320     boolean is_backside = object_mapping[i].is_backside;
7321     boolean action_exploding = ((action == ACTION_EXPLODING ||
7322                                  action == ACTION_SMASHED_BY_ROCK ||
7323                                  action == ACTION_SMASHED_BY_SPRING) &&
7324                                 element != EL_DIAMOND);
7325     boolean action_active = (action == ACTION_ACTIVE);
7326     boolean action_other = (action == ACTION_OTHER);
7327
7328     for (j = 0; j < 8; j++)
7329     {
7330       int effective_element = get_effective_element_EM(i, j);
7331       int effective_action = (j < 7 ? action :
7332                               i == Xdrip_stretch ? action :
7333                               i == Xdrip_stretchB ? action :
7334                               i == Ydrip_s1 ? action :
7335                               i == Ydrip_s1B ? action :
7336                               i == Xball_1B ? action :
7337                               i == Xball_2 ? action :
7338                               i == Xball_2B ? action :
7339                               i == Yball_eat ? action :
7340                               i == Ykey_1_eat ? action :
7341                               i == Ykey_2_eat ? action :
7342                               i == Ykey_3_eat ? action :
7343                               i == Ykey_4_eat ? action :
7344                               i == Ykey_5_eat ? action :
7345                               i == Ykey_6_eat ? action :
7346                               i == Ykey_7_eat ? action :
7347                               i == Ykey_8_eat ? action :
7348                               i == Ylenses_eat ? action :
7349                               i == Ymagnify_eat ? action :
7350                               i == Ygrass_eat ? action :
7351                               i == Ydirt_eat ? action :
7352                               i == Xsand_stonein_1 ? action :
7353                               i == Xsand_stonein_2 ? action :
7354                               i == Xsand_stonein_3 ? action :
7355                               i == Xsand_stonein_4 ? action :
7356                               i == Xsand_stoneout_1 ? action :
7357                               i == Xsand_stoneout_2 ? action :
7358                               i == Xboom_android ? ACTION_EXPLODING :
7359                               action_exploding ? ACTION_EXPLODING :
7360                               action_active ? action :
7361                               action_other ? action :
7362                               ACTION_DEFAULT);
7363       int graphic = (el_act_dir2img(effective_element, effective_action,
7364                                     direction));
7365       int crumbled = (el_act_dir2crm(effective_element, effective_action,
7366                                      direction));
7367       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7368       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7369       boolean has_action_graphics = (graphic != base_graphic);
7370       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7371       struct GraphicInfo *g = &graphic_info[graphic];
7372       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7373       Bitmap *src_bitmap;
7374       int src_x, src_y;
7375       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7376       boolean special_animation = (action != ACTION_DEFAULT &&
7377                                    g->anim_frames == 3 &&
7378                                    g->anim_delay == 2 &&
7379                                    g->anim_mode & ANIM_LINEAR);
7380       int sync_frame = (i == Xdrip_stretch ? 7 :
7381                         i == Xdrip_stretchB ? 7 :
7382                         i == Ydrip_s2 ? j + 8 :
7383                         i == Ydrip_s2B ? j + 8 :
7384                         i == Xacid_1 ? 0 :
7385                         i == Xacid_2 ? 10 :
7386                         i == Xacid_3 ? 20 :
7387                         i == Xacid_4 ? 30 :
7388                         i == Xacid_5 ? 40 :
7389                         i == Xacid_6 ? 50 :
7390                         i == Xacid_7 ? 60 :
7391                         i == Xacid_8 ? 70 :
7392                         i == Xfake_acid_1 ? 0 :
7393                         i == Xfake_acid_2 ? 10 :
7394                         i == Xfake_acid_3 ? 20 :
7395                         i == Xfake_acid_4 ? 30 :
7396                         i == Xfake_acid_5 ? 40 :
7397                         i == Xfake_acid_6 ? 50 :
7398                         i == Xfake_acid_7 ? 60 :
7399                         i == Xfake_acid_8 ? 70 :
7400                         i == Xball_2 ? 7 :
7401                         i == Xball_2B ? j + 8 :
7402                         i == Yball_eat ? j + 1 :
7403                         i == Ykey_1_eat ? j + 1 :
7404                         i == Ykey_2_eat ? j + 1 :
7405                         i == Ykey_3_eat ? j + 1 :
7406                         i == Ykey_4_eat ? j + 1 :
7407                         i == Ykey_5_eat ? j + 1 :
7408                         i == Ykey_6_eat ? j + 1 :
7409                         i == Ykey_7_eat ? j + 1 :
7410                         i == Ykey_8_eat ? j + 1 :
7411                         i == Ylenses_eat ? j + 1 :
7412                         i == Ymagnify_eat ? j + 1 :
7413                         i == Ygrass_eat ? j + 1 :
7414                         i == Ydirt_eat ? j + 1 :
7415                         i == Xamoeba_1 ? 0 :
7416                         i == Xamoeba_2 ? 1 :
7417                         i == Xamoeba_3 ? 2 :
7418                         i == Xamoeba_4 ? 3 :
7419                         i == Xamoeba_5 ? 0 :
7420                         i == Xamoeba_6 ? 1 :
7421                         i == Xamoeba_7 ? 2 :
7422                         i == Xamoeba_8 ? 3 :
7423                         i == Xexit_2 ? j + 8 :
7424                         i == Xexit_3 ? j + 16 :
7425                         i == Xdynamite_1 ? 0 :
7426                         i == Xdynamite_2 ? 8 :
7427                         i == Xdynamite_3 ? 16 :
7428                         i == Xdynamite_4 ? 24 :
7429                         i == Xsand_stonein_1 ? j + 1 :
7430                         i == Xsand_stonein_2 ? j + 9 :
7431                         i == Xsand_stonein_3 ? j + 17 :
7432                         i == Xsand_stonein_4 ? j + 25 :
7433                         i == Xsand_stoneout_1 && j == 0 ? 0 :
7434                         i == Xsand_stoneout_1 && j == 1 ? 0 :
7435                         i == Xsand_stoneout_1 && j == 2 ? 1 :
7436                         i == Xsand_stoneout_1 && j == 3 ? 2 :
7437                         i == Xsand_stoneout_1 && j == 4 ? 2 :
7438                         i == Xsand_stoneout_1 && j == 5 ? 3 :
7439                         i == Xsand_stoneout_1 && j == 6 ? 4 :
7440                         i == Xsand_stoneout_1 && j == 7 ? 4 :
7441                         i == Xsand_stoneout_2 && j == 0 ? 5 :
7442                         i == Xsand_stoneout_2 && j == 1 ? 6 :
7443                         i == Xsand_stoneout_2 && j == 2 ? 7 :
7444                         i == Xsand_stoneout_2 && j == 3 ? 8 :
7445                         i == Xsand_stoneout_2 && j == 4 ? 9 :
7446                         i == Xsand_stoneout_2 && j == 5 ? 11 :
7447                         i == Xsand_stoneout_2 && j == 6 ? 13 :
7448                         i == Xsand_stoneout_2 && j == 7 ? 15 :
7449                         i == Xboom_bug && j == 1 ? 2 :
7450                         i == Xboom_bug && j == 2 ? 2 :
7451                         i == Xboom_bug && j == 3 ? 4 :
7452                         i == Xboom_bug && j == 4 ? 4 :
7453                         i == Xboom_bug && j == 5 ? 2 :
7454                         i == Xboom_bug && j == 6 ? 2 :
7455                         i == Xboom_bug && j == 7 ? 0 :
7456                         i == Xboom_bomb && j == 1 ? 2 :
7457                         i == Xboom_bomb && j == 2 ? 2 :
7458                         i == Xboom_bomb && j == 3 ? 4 :
7459                         i == Xboom_bomb && j == 4 ? 4 :
7460                         i == Xboom_bomb && j == 5 ? 2 :
7461                         i == Xboom_bomb && j == 6 ? 2 :
7462                         i == Xboom_bomb && j == 7 ? 0 :
7463                         i == Xboom_android && j == 7 ? 6 :
7464                         i == Xboom_1 && j == 1 ? 2 :
7465                         i == Xboom_1 && j == 2 ? 2 :
7466                         i == Xboom_1 && j == 3 ? 4 :
7467                         i == Xboom_1 && j == 4 ? 4 :
7468                         i == Xboom_1 && j == 5 ? 6 :
7469                         i == Xboom_1 && j == 6 ? 6 :
7470                         i == Xboom_1 && j == 7 ? 8 :
7471                         i == Xboom_2 && j == 0 ? 8 :
7472                         i == Xboom_2 && j == 1 ? 8 :
7473                         i == Xboom_2 && j == 2 ? 10 :
7474                         i == Xboom_2 && j == 3 ? 10 :
7475                         i == Xboom_2 && j == 4 ? 10 :
7476                         i == Xboom_2 && j == 5 ? 12 :
7477                         i == Xboom_2 && j == 6 ? 12 :
7478                         i == Xboom_2 && j == 7 ? 12 :
7479                         special_animation && j == 4 ? 3 :
7480                         effective_action != action ? 0 :
7481                         j);
7482
7483 #if DEBUG_EM_GFX
7484       Bitmap *debug_bitmap = g_em->bitmap;
7485       int debug_src_x = g_em->src_x;
7486       int debug_src_y = g_em->src_y;
7487 #endif
7488
7489       int frame = getAnimationFrame(g->anim_frames,
7490                                     g->anim_delay,
7491                                     g->anim_mode,
7492                                     g->anim_start_frame,
7493                                     sync_frame);
7494
7495       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7496                           g->double_movement && is_backside);
7497
7498       g_em->bitmap = src_bitmap;
7499       g_em->src_x = src_x;
7500       g_em->src_y = src_y;
7501       g_em->src_offset_x = 0;
7502       g_em->src_offset_y = 0;
7503       g_em->dst_offset_x = 0;
7504       g_em->dst_offset_y = 0;
7505       g_em->width  = TILEX;
7506       g_em->height = TILEY;
7507
7508       g_em->preserve_background = FALSE;
7509
7510       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7511                                sync_frame);
7512
7513       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7514                                    effective_action == ACTION_MOVING  ||
7515                                    effective_action == ACTION_PUSHING ||
7516                                    effective_action == ACTION_EATING)) ||
7517           (!has_action_graphics && (effective_action == ACTION_FILLING ||
7518                                     effective_action == ACTION_EMPTYING)))
7519       {
7520         int move_dir =
7521           (effective_action == ACTION_FALLING ||
7522            effective_action == ACTION_FILLING ||
7523            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7524         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7525         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
7526         int num_steps = (i == Ydrip_s1  ? 16 :
7527                          i == Ydrip_s1B ? 16 :
7528                          i == Ydrip_s2  ? 16 :
7529                          i == Ydrip_s2B ? 16 :
7530                          i == Xsand_stonein_1 ? 32 :
7531                          i == Xsand_stonein_2 ? 32 :
7532                          i == Xsand_stonein_3 ? 32 :
7533                          i == Xsand_stonein_4 ? 32 :
7534                          i == Xsand_stoneout_1 ? 16 :
7535                          i == Xsand_stoneout_2 ? 16 : 8);
7536         int cx = ABS(dx) * (TILEX / num_steps);
7537         int cy = ABS(dy) * (TILEY / num_steps);
7538         int step_frame = (i == Ydrip_s2         ? j + 8 :
7539                           i == Ydrip_s2B        ? j + 8 :
7540                           i == Xsand_stonein_2  ? j + 8 :
7541                           i == Xsand_stonein_3  ? j + 16 :
7542                           i == Xsand_stonein_4  ? j + 24 :
7543                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7544         int step = (is_backside ? step_frame : num_steps - step_frame);
7545
7546         if (is_backside)        /* tile where movement starts */
7547         {
7548           if (dx < 0 || dy < 0)
7549           {
7550             g_em->src_offset_x = cx * step;
7551             g_em->src_offset_y = cy * step;
7552           }
7553           else
7554           {
7555             g_em->dst_offset_x = cx * step;
7556             g_em->dst_offset_y = cy * step;
7557           }
7558         }
7559         else                    /* tile where movement ends */
7560         {
7561           if (dx < 0 || dy < 0)
7562           {
7563             g_em->dst_offset_x = cx * step;
7564             g_em->dst_offset_y = cy * step;
7565           }
7566           else
7567           {
7568             g_em->src_offset_x = cx * step;
7569             g_em->src_offset_y = cy * step;
7570           }
7571         }
7572
7573         g_em->width  = TILEX - cx * step;
7574         g_em->height = TILEY - cy * step;
7575       }
7576
7577       /* create unique graphic identifier to decide if tile must be redrawn */
7578       /* bit 31 - 16 (16 bit): EM style graphic
7579          bit 15 - 12 ( 4 bit): EM style frame
7580          bit 11 -  6 ( 6 bit): graphic width
7581          bit  5 -  0 ( 6 bit): graphic height */
7582       g_em->unique_identifier =
7583         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7584
7585 #if DEBUG_EM_GFX
7586
7587       /* skip check for EMC elements not contained in original EMC artwork */
7588       if (element == EL_EMC_FAKE_ACID)
7589         continue;
7590
7591       if (g_em->bitmap != debug_bitmap ||
7592           g_em->src_x != debug_src_x ||
7593           g_em->src_y != debug_src_y ||
7594           g_em->src_offset_x != 0 ||
7595           g_em->src_offset_y != 0 ||
7596           g_em->dst_offset_x != 0 ||
7597           g_em->dst_offset_y != 0 ||
7598           g_em->width != TILEX ||
7599           g_em->height != TILEY)
7600       {
7601         static int last_i = -1;
7602
7603         if (i != last_i)
7604         {
7605           printf("\n");
7606           last_i = i;
7607         }
7608
7609         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7610                i, element, element_info[element].token_name,
7611                element_action_info[effective_action].suffix, direction);
7612
7613         if (element != effective_element)
7614           printf(" [%d ('%s')]",
7615                  effective_element,
7616                  element_info[effective_element].token_name);
7617
7618         printf("\n");
7619
7620         if (g_em->bitmap != debug_bitmap)
7621           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7622                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7623
7624         if (g_em->src_x != debug_src_x ||
7625             g_em->src_y != debug_src_y)
7626           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7627                  j, (is_backside ? 'B' : 'F'),
7628                  g_em->src_x, g_em->src_y,
7629                  g_em->src_x / 32, g_em->src_y / 32,
7630                  debug_src_x, debug_src_y,
7631                  debug_src_x / 32, debug_src_y / 32);
7632
7633         if (g_em->src_offset_x != 0 ||
7634             g_em->src_offset_y != 0 ||
7635             g_em->dst_offset_x != 0 ||
7636             g_em->dst_offset_y != 0)
7637           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7638                  j, is_backside,
7639                  g_em->src_offset_x, g_em->src_offset_y,
7640                  g_em->dst_offset_x, g_em->dst_offset_y);
7641
7642         if (g_em->width != TILEX ||
7643             g_em->height != TILEY)
7644           printf("    %d (%d): size %d,%d should be %d,%d\n",
7645                  j, is_backside,
7646                  g_em->width, g_em->height, TILEX, TILEY);
7647
7648         num_em_gfx_errors++;
7649       }
7650 #endif
7651
7652     }
7653   }
7654
7655   for (i = 0; i < TILE_MAX; i++)
7656   {
7657     for (j = 0; j < 8; j++)
7658     {
7659       int element = object_mapping[i].element_rnd;
7660       int action = object_mapping[i].action;
7661       int direction = object_mapping[i].direction;
7662       boolean is_backside = object_mapping[i].is_backside;
7663       int graphic_action  = el_act_dir2img(element, action, direction);
7664       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7665
7666       if ((action == ACTION_SMASHED_BY_ROCK ||
7667            action == ACTION_SMASHED_BY_SPRING ||
7668            action == ACTION_EATING) &&
7669           graphic_action == graphic_default)
7670       {
7671         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
7672                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7673                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
7674                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7675                  Xspring);
7676
7677         /* no separate animation for "smashed by rock" -- use rock instead */
7678         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7679         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7680
7681         g_em->bitmap            = g_xx->bitmap;
7682         g_em->src_x             = g_xx->src_x;
7683         g_em->src_y             = g_xx->src_y;
7684         g_em->src_offset_x      = g_xx->src_offset_x;
7685         g_em->src_offset_y      = g_xx->src_offset_y;
7686         g_em->dst_offset_x      = g_xx->dst_offset_x;
7687         g_em->dst_offset_y      = g_xx->dst_offset_y;
7688         g_em->width             = g_xx->width;
7689         g_em->height            = g_xx->height;
7690         g_em->unique_identifier = g_xx->unique_identifier;
7691
7692         if (!is_backside)
7693           g_em->preserve_background = TRUE;
7694       }
7695     }
7696   }
7697
7698   for (p = 0; p < MAX_PLAYERS; p++)
7699   {
7700     for (i = 0; i < SPR_MAX; i++)
7701     {
7702       int element = player_mapping[p][i].element_rnd;
7703       int action = player_mapping[p][i].action;
7704       int direction = player_mapping[p][i].direction;
7705
7706       for (j = 0; j < 8; j++)
7707       {
7708         int effective_element = element;
7709         int effective_action = action;
7710         int graphic = (direction == MV_NONE ?
7711                        el_act2img(effective_element, effective_action) :
7712                        el_act_dir2img(effective_element, effective_action,
7713                                       direction));
7714         struct GraphicInfo *g = &graphic_info[graphic];
7715         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7716         Bitmap *src_bitmap;
7717         int src_x, src_y;
7718         int sync_frame = j;
7719
7720 #if DEBUG_EM_GFX
7721         Bitmap *debug_bitmap = g_em->bitmap;
7722         int debug_src_x = g_em->src_x;
7723         int debug_src_y = g_em->src_y;
7724 #endif
7725
7726         int frame = getAnimationFrame(g->anim_frames,
7727                                       g->anim_delay,
7728                                       g->anim_mode,
7729                                       g->anim_start_frame,
7730                                       sync_frame);
7731
7732         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7733
7734         g_em->bitmap = src_bitmap;
7735         g_em->src_x = src_x;
7736         g_em->src_y = src_y;
7737         g_em->src_offset_x = 0;
7738         g_em->src_offset_y = 0;
7739         g_em->dst_offset_x = 0;
7740         g_em->dst_offset_y = 0;
7741         g_em->width  = TILEX;
7742         g_em->height = TILEY;
7743
7744 #if DEBUG_EM_GFX
7745
7746         /* skip check for EMC elements not contained in original EMC artwork */
7747         if (element == EL_PLAYER_3 ||
7748             element == EL_PLAYER_4)
7749           continue;
7750
7751         if (g_em->bitmap != debug_bitmap ||
7752             g_em->src_x != debug_src_x ||
7753             g_em->src_y != debug_src_y)
7754         {
7755           static int last_i = -1;
7756
7757           if (i != last_i)
7758           {
7759             printf("\n");
7760             last_i = i;
7761           }
7762
7763           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7764                  p, i, element, element_info[element].token_name,
7765                  element_action_info[effective_action].suffix, direction);
7766
7767           if (element != effective_element)
7768             printf(" [%d ('%s')]",
7769                    effective_element,
7770                    element_info[effective_element].token_name);
7771
7772           printf("\n");
7773
7774           if (g_em->bitmap != debug_bitmap)
7775             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
7776                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
7777
7778           if (g_em->src_x != debug_src_x ||
7779               g_em->src_y != debug_src_y)
7780             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7781                    j,
7782                    g_em->src_x, g_em->src_y,
7783                    g_em->src_x / 32, g_em->src_y / 32,
7784                    debug_src_x, debug_src_y,
7785                    debug_src_x / 32, debug_src_y / 32);
7786
7787           num_em_gfx_errors++;
7788         }
7789 #endif
7790
7791       }
7792     }
7793   }
7794
7795 #if DEBUG_EM_GFX
7796   printf("\n");
7797   printf("::: [%d errors found]\n", num_em_gfx_errors);
7798
7799   exit(0);
7800 #endif
7801 }
7802
7803 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7804                                 boolean any_player_moving,
7805                                 boolean any_player_snapping,
7806                                 boolean any_player_dropping)
7807 {
7808   static boolean player_was_waiting = TRUE;
7809
7810   if (frame == 0 && !any_player_dropping)
7811   {
7812     if (!player_was_waiting)
7813     {
7814       if (!SaveEngineSnapshotToList())
7815         return;
7816
7817       player_was_waiting = TRUE;
7818     }
7819   }
7820   else if (any_player_moving || any_player_snapping || any_player_dropping)
7821   {
7822     player_was_waiting = FALSE;
7823   }
7824 }
7825
7826 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7827                                 boolean murphy_is_dropping)
7828 {
7829   static boolean player_was_waiting = TRUE;
7830
7831   if (murphy_is_waiting)
7832   {
7833     if (!player_was_waiting)
7834     {
7835       if (!SaveEngineSnapshotToList())
7836         return;
7837
7838       player_was_waiting = TRUE;
7839     }
7840   }
7841   else
7842   {
7843     player_was_waiting = FALSE;
7844   }
7845 }
7846
7847 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7848                             boolean any_player_moving,
7849                             boolean any_player_snapping,
7850                             boolean any_player_dropping)
7851 {
7852   if (tape.single_step && tape.recording && !tape.pausing)
7853     if (frame == 0 && !any_player_dropping)
7854       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7855
7856   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7857                              any_player_snapping, any_player_dropping);
7858 }
7859
7860 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7861                             boolean murphy_is_dropping)
7862 {
7863   if (tape.single_step && tape.recording && !tape.pausing)
7864     if (murphy_is_waiting)
7865       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7866
7867   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7868 }
7869
7870 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7871                          int graphic, int sync_frame, int x, int y)
7872 {
7873   int frame = getGraphicAnimationFrame(graphic, sync_frame);
7874
7875   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7876 }
7877
7878 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7879 {
7880   return (IS_NEXT_FRAME(sync_frame, graphic));
7881 }
7882
7883 int getGraphicInfo_Delay(int graphic)
7884 {
7885   return graphic_info[graphic].anim_delay;
7886 }
7887
7888 void PlayMenuSoundExt(int sound)
7889 {
7890   if (sound == SND_UNDEFINED)
7891     return;
7892
7893   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7894       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7895     return;
7896
7897   if (IS_LOOP_SOUND(sound))
7898     PlaySoundLoop(sound);
7899   else
7900     PlaySound(sound);
7901 }
7902
7903 void PlayMenuSound()
7904 {
7905   PlayMenuSoundExt(menu.sound[game_status]);
7906 }
7907
7908 void PlayMenuSoundStereo(int sound, int stereo_position)
7909 {
7910   if (sound == SND_UNDEFINED)
7911     return;
7912
7913   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7914       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7915     return;
7916
7917   if (IS_LOOP_SOUND(sound))
7918     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7919   else
7920     PlaySoundStereo(sound, stereo_position);
7921 }
7922
7923 void PlayMenuSoundIfLoopExt(int sound)
7924 {
7925   if (sound == SND_UNDEFINED)
7926     return;
7927
7928   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7929       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7930     return;
7931
7932   if (IS_LOOP_SOUND(sound))
7933     PlaySoundLoop(sound);
7934 }
7935
7936 void PlayMenuSoundIfLoop()
7937 {
7938   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7939 }
7940
7941 void PlayMenuMusicExt(int music)
7942 {
7943   if (music == MUS_UNDEFINED)
7944     return;
7945
7946   if (!setup.sound_music)
7947     return;
7948
7949   PlayMusic(music);
7950 }
7951
7952 void PlayMenuMusic()
7953 {
7954   PlayMenuMusicExt(menu.music[game_status]);
7955 }
7956
7957 void PlaySoundActivating()
7958 {
7959 #if 0
7960   PlaySound(SND_MENU_ITEM_ACTIVATING);
7961 #endif
7962 }
7963
7964 void PlaySoundSelecting()
7965 {
7966 #if 0
7967   PlaySound(SND_MENU_ITEM_SELECTING);
7968 #endif
7969 }
7970
7971 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7972 {
7973   boolean change_fullscreen = (setup.fullscreen !=
7974                                video.fullscreen_enabled);
7975   boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7976                                     !strEqual(setup.fullscreen_mode,
7977                                               video.fullscreen_mode_current));
7978   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7979                                            setup.window_scaling_percent !=
7980                                            video.window_scaling_percent);
7981
7982   if (change_window_scaling_percent && video.fullscreen_enabled)
7983     return;
7984
7985   if (!change_window_scaling_percent && !video.fullscreen_available)
7986     return;
7987
7988 #if defined(TARGET_SDL2)
7989   if (change_window_scaling_percent)
7990   {
7991     SDLSetWindowScaling(setup.window_scaling_percent);
7992
7993     return;
7994   }
7995   else if (change_fullscreen)
7996   {
7997     SDLSetWindowFullscreen(setup.fullscreen);
7998
7999     /* set setup value according to successfully changed fullscreen mode */
8000     setup.fullscreen = video.fullscreen_enabled;
8001
8002     return;
8003   }
8004 #endif
8005
8006   if (change_fullscreen ||
8007       change_fullscreen_mode ||
8008       change_window_scaling_percent)
8009   {
8010     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8011
8012     /* save backbuffer content which gets lost when toggling fullscreen mode */
8013     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8014
8015     if (change_fullscreen_mode)
8016     {
8017       /* keep fullscreen, but change fullscreen mode (screen resolution) */
8018       video.fullscreen_enabled = FALSE;         /* force new fullscreen mode */
8019     }
8020
8021     if (change_window_scaling_percent)
8022     {
8023       /* keep window mode, but change window scaling */
8024       video.fullscreen_enabled = TRUE;          /* force new window scaling */
8025     }
8026
8027     /* toggle fullscreen */
8028     ChangeVideoModeIfNeeded(setup.fullscreen);
8029
8030     /* set setup value according to successfully changed fullscreen mode */
8031     setup.fullscreen = video.fullscreen_enabled;
8032
8033     /* restore backbuffer content from temporary backbuffer backup bitmap */
8034     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8035
8036     FreeBitmap(tmp_backbuffer);
8037
8038     /* update visible window/screen */
8039     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8040   }
8041 }
8042
8043 void ChangeViewportPropertiesIfNeeded()
8044 {
8045   int gfx_game_mode = game_status;
8046   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8047                         game_status);
8048   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8049   struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8050   struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8051   struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8052   int border_size       = vp_playfield->border_size;
8053   int new_sx            = vp_playfield->x + border_size;
8054   int new_sy            = vp_playfield->y + border_size;
8055   int new_sxsize        = vp_playfield->width  - 2 * border_size;
8056   int new_sysize        = vp_playfield->height - 2 * border_size;
8057   int new_real_sx       = vp_playfield->x;
8058   int new_real_sy       = vp_playfield->y;
8059   int new_full_sxsize   = vp_playfield->width;
8060   int new_full_sysize   = vp_playfield->height;
8061   int new_dx            = vp_door_1->x;
8062   int new_dy            = vp_door_1->y;
8063   int new_dxsize        = vp_door_1->width;
8064   int new_dysize        = vp_door_1->height;
8065   int new_vx            = vp_door_2->x;
8066   int new_vy            = vp_door_2->y;
8067   int new_vxsize        = vp_door_2->width;
8068   int new_vysize        = vp_door_2->height;
8069   int new_ex            = vp_door_3->x;
8070   int new_ey            = vp_door_3->y;
8071   int new_exsize        = vp_door_3->width;
8072   int new_eysize        = vp_door_3->height;
8073   int new_tilesize_var =
8074     (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8075
8076   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8077                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8078   int new_scr_fieldx = new_sxsize / tilesize;
8079   int new_scr_fieldy = new_sysize / tilesize;
8080   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8081   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8082   boolean init_gfx_buffers = FALSE;
8083   boolean init_video_buffer = FALSE;
8084   boolean init_gadgets_and_toons = FALSE;
8085   boolean init_em_graphics = FALSE;
8086   boolean drawing_area_changed = FALSE;
8087
8088   if (viewport.window.width  != WIN_XSIZE ||
8089       viewport.window.height != WIN_YSIZE)
8090   {
8091     WIN_XSIZE = viewport.window.width;
8092     WIN_YSIZE = viewport.window.height;
8093
8094     init_video_buffer = TRUE;
8095     init_gfx_buffers = TRUE;
8096
8097     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8098   }
8099
8100   if (new_scr_fieldx != SCR_FIELDX ||
8101       new_scr_fieldy != SCR_FIELDY)
8102   {
8103     /* this always toggles between MAIN and GAME when using small tile size */
8104
8105     SCR_FIELDX = new_scr_fieldx;
8106     SCR_FIELDY = new_scr_fieldy;
8107
8108     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8109   }
8110
8111   if (new_sx != SX ||
8112       new_sy != SY ||
8113       new_dx != DX ||
8114       new_dy != DY ||
8115       new_vx != VX ||
8116       new_vy != VY ||
8117       new_ex != EX ||
8118       new_ey != EY ||
8119       new_sxsize != SXSIZE ||
8120       new_sysize != SYSIZE ||
8121       new_dxsize != DXSIZE ||
8122       new_dysize != DYSIZE ||
8123       new_vxsize != VXSIZE ||
8124       new_vysize != VYSIZE ||
8125       new_exsize != EXSIZE ||
8126       new_eysize != EYSIZE ||
8127       new_real_sx != REAL_SX ||
8128       new_real_sy != REAL_SY ||
8129       new_full_sxsize != FULL_SXSIZE ||
8130       new_full_sysize != FULL_SYSIZE ||
8131       new_tilesize_var != TILESIZE_VAR
8132       )
8133   {
8134     if (new_tilesize_var != TILESIZE_VAR)
8135     {
8136       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8137
8138       // changing tile size invalidates scroll values of engine snapshots
8139       FreeEngineSnapshotSingle();
8140
8141       // changing tile size requires update of graphic mapping for EM engine
8142       init_em_graphics = TRUE;
8143     }
8144
8145     if (new_sx != SX ||
8146         new_sy != SY ||
8147         new_sxsize != SXSIZE ||
8148         new_sysize != SYSIZE ||
8149         new_real_sx != REAL_SX ||
8150         new_real_sy != REAL_SY ||
8151         new_full_sxsize != FULL_SXSIZE ||
8152         new_full_sysize != FULL_SYSIZE)
8153     {
8154       if (!init_video_buffer)
8155         drawing_area_changed = TRUE;
8156     }
8157
8158     SX = new_sx;
8159     SY = new_sy;
8160     DX = new_dx;
8161     DY = new_dy;
8162     VX = new_vx;
8163     VY = new_vy;
8164     EX = new_ex;
8165     EY = new_ey;
8166     SXSIZE = new_sxsize;
8167     SYSIZE = new_sysize;
8168     DXSIZE = new_dxsize;
8169     DYSIZE = new_dysize;
8170     VXSIZE = new_vxsize;
8171     VYSIZE = new_vysize;
8172     EXSIZE = new_exsize;
8173     EYSIZE = new_eysize;
8174     REAL_SX = new_real_sx;
8175     REAL_SY = new_real_sy;
8176     FULL_SXSIZE = new_full_sxsize;
8177     FULL_SYSIZE = new_full_sysize;
8178     TILESIZE_VAR = new_tilesize_var;
8179
8180     init_gfx_buffers = TRUE;
8181     init_gadgets_and_toons = TRUE;
8182
8183     // printf("::: viewports: init_gfx_buffers\n");
8184     // printf("::: viewports: init_gadgets_and_toons\n");
8185   }
8186
8187   if (init_gfx_buffers)
8188   {
8189     // printf("::: init_gfx_buffers\n");
8190
8191     SCR_FIELDX = new_scr_fieldx_buffers;
8192     SCR_FIELDY = new_scr_fieldy_buffers;
8193
8194     InitGfxBuffers();
8195
8196     SCR_FIELDX = new_scr_fieldx;
8197     SCR_FIELDY = new_scr_fieldy;
8198
8199     gfx.drawing_area_changed = drawing_area_changed;
8200
8201     SetDrawDeactivationMask(REDRAW_NONE);
8202     SetDrawBackgroundMask(REDRAW_FIELD);
8203   }
8204
8205   if (init_video_buffer)
8206   {
8207     // printf("::: init_video_buffer\n");
8208
8209     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8210   }
8211
8212   if (init_gadgets_and_toons)
8213   {
8214     // printf("::: init_gadgets_and_toons\n");
8215
8216     InitGadgets();
8217     InitToons();
8218   }
8219
8220   if (init_em_graphics)
8221   {
8222       InitGraphicInfo_EM();
8223   }
8224 }