added movement-based engine snapshot mode to step-based mode
[rocksndiamonds.git] / src / tools.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // tools.c
10 // ============================================================================
11
12 #include <math.h>
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "init.h"
18 #include "game.h"
19 #include "events.h"
20 #include "cartoons.h"
21 #include "network.h"
22 #include "tape.h"
23 #include "screens.h"
24
25
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX    0
28
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES        0
31 #define TOOL_CTRL_ID_NO         1
32 #define TOOL_CTRL_ID_CONFIRM    2
33 #define TOOL_CTRL_ID_PLAYER_1   3
34 #define TOOL_CTRL_ID_PLAYER_2   4
35 #define TOOL_CTRL_ID_PLAYER_3   5
36 #define TOOL_CTRL_ID_PLAYER_4   6
37
38 #define NUM_TOOL_BUTTONS        7
39
40 /* constants for number of doors and door parts */
41 #define NUM_DOORS               2
42 #define NUM_PANELS              NUM_DOORS
43 // #define NUM_PANELS           0
44 #define MAX_PARTS_PER_DOOR      8
45 #define MAX_DOOR_PARTS          (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i)   ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
47
48
49 struct DoorPartOrderInfo
50 {
51   int nr;
52   int sort_priority;
53 };
54
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
56
57 struct DoorPartControlInfo
58 {
59   int door_token;
60   int graphic;
61   struct DoorPartPosInfo *pos;
62 };
63
64 static struct DoorPartControlInfo door_part_controls[] =
65 {
66   {
67     DOOR_1,
68     IMG_DOOR_1_GFX_PART_1,
69     &door_1.part_1
70   },
71   {
72     DOOR_1,
73     IMG_DOOR_1_GFX_PART_2,
74     &door_1.part_2
75   },
76   {
77     DOOR_1,
78     IMG_DOOR_1_GFX_PART_3,
79     &door_1.part_3
80   },
81   {
82     DOOR_1,
83     IMG_DOOR_1_GFX_PART_4,
84     &door_1.part_4
85   },
86   {
87     DOOR_1,
88     IMG_DOOR_1_GFX_PART_5,
89     &door_1.part_5
90   },
91   {
92     DOOR_1,
93     IMG_DOOR_1_GFX_PART_6,
94     &door_1.part_6
95   },
96   {
97     DOOR_1,
98     IMG_DOOR_1_GFX_PART_7,
99     &door_1.part_7
100   },
101   {
102     DOOR_1,
103     IMG_DOOR_1_GFX_PART_8,
104     &door_1.part_8
105   },
106
107   {
108     DOOR_2,
109     IMG_DOOR_2_GFX_PART_1,
110     &door_2.part_1
111   },
112   {
113     DOOR_2,
114     IMG_DOOR_2_GFX_PART_2,
115     &door_2.part_2
116   },
117   {
118     DOOR_2,
119     IMG_DOOR_2_GFX_PART_3,
120     &door_2.part_3
121   },
122   {
123     DOOR_2,
124     IMG_DOOR_2_GFX_PART_4,
125     &door_2.part_4
126   },
127   {
128     DOOR_2,
129     IMG_DOOR_2_GFX_PART_5,
130     &door_2.part_5
131   },
132   {
133     DOOR_2,
134     IMG_DOOR_2_GFX_PART_6,
135     &door_2.part_6
136   },
137   {
138     DOOR_2,
139     IMG_DOOR_2_GFX_PART_7,
140     &door_2.part_7
141   },
142   {
143     DOOR_2,
144     IMG_DOOR_2_GFX_PART_8,
145     &door_2.part_8
146   },
147
148   {
149     DOOR_1,
150     IMG_BACKGROUND_PANEL,
151     &door_1.panel
152   },
153   {
154     DOOR_2,
155     IMG_BACKGROUND_TAPE,
156     &door_2.panel
157   },
158
159   {
160     -1,
161     -1,
162     NULL
163   }
164 };
165
166
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
172
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
175
176 static char *print_if_not_empty(int element)
177 {
178   static char *s = NULL;
179   char *token_name = element_info[element].token_name;
180
181   if (s != NULL)
182     free(s);
183
184   s = checked_malloc(strlen(token_name) + 10 + 1);
185
186   if (element != EL_EMPTY)
187     sprintf(s, "%d\t['%s']", element, token_name);
188   else
189     sprintf(s, "%d", element);
190
191   return s;
192 }
193
194 void DumpTile(int x, int y)
195 {
196   int sx = SCREENX(x);
197   int sy = SCREENY(y);
198
199   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
200   {
201     x--;
202     y--;
203   }
204
205   printf_line("-", 79);
206   printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207   printf_line("-", 79);
208
209   if (!IN_LEV_FIELD(x, y))
210   {
211     printf("(not in level field)\n");
212     printf("\n");
213
214     return;
215   }
216
217   printf("  Feld:        %d\t['%s']\n", Feld[x][y],
218          element_info[Feld[x][y]].token_name);
219   printf("  Back:        %s\n", print_if_not_empty(Back[x][y]));
220   printf("  Store:       %s\n", print_if_not_empty(Store[x][y]));
221   printf("  Store2:      %s\n", print_if_not_empty(Store2[x][y]));
222   printf("  StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223   printf("  MovPos:      %d\n", MovPos[x][y]);
224   printf("  MovDir:      %d\n", MovDir[x][y]);
225   printf("  MovDelay:    %d\n", MovDelay[x][y]);
226   printf("  ChangeDelay: %d\n", ChangeDelay[x][y]);
227   printf("  CustomValue: %d\n", CustomValue[x][y]);
228   printf("  GfxElement:  %d\n", GfxElement[x][y]);
229   printf("  GfxAction:   %d\n", GfxAction[x][y]);
230   printf("  GfxFrame:    %d\n", GfxFrame[x][y]);
231   printf("\n");
232 }
233
234 void SetDrawtoField(int mode)
235 {
236   if (mode == DRAW_BUFFERED && setup.soft_scrolling)
237   {
238     FX = 2 * TILEX_VAR;
239     FY = 2 * TILEY_VAR;
240     BX1 = -2;
241     BY1 = -2;
242     BX2 = SCR_FIELDX + 1;
243     BY2 = SCR_FIELDY + 1;
244     redraw_x1 = 2;
245     redraw_y1 = 2;
246
247     drawto_field = fieldbuffer;
248   }
249   else  /* DRAW_BACKBUFFER */
250   {
251     FX = SX;
252     FY = SY;
253     BX1 = 0;
254     BY1 = 0;
255     BX2 = SCR_FIELDX - 1;
256     BY2 = SCR_FIELDY - 1;
257     redraw_x1 = 0;
258     redraw_y1 = 0;
259
260     drawto_field = backbuffer;
261   }
262 }
263
264 static void RedrawPlayfield_RND()
265 {
266   if (game.envelope_active)
267     return;
268
269   DrawLevel(REDRAW_ALL);
270   DrawAllPlayers();
271 }
272
273 void RedrawPlayfield()
274 {
275   if (game_status != GAME_MODE_PLAYING)
276     return;
277
278   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
279     RedrawPlayfield_EM(TRUE);
280   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
281     RedrawPlayfield_SP(TRUE);
282   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
283     RedrawPlayfield_RND();
284
285   BlitScreenToBitmap(backbuffer);
286
287   BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
288              gfx.sx, gfx.sy);
289 }
290
291 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
292 {
293   Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
294
295   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 (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
955   {
956     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
957     SetDrawtoField(DRAW_BUFFERED);
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);
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 x, y;
2143
2144   for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2145   {
2146     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2147     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2148     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2149     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2150     int xx, yy;
2151
2152     SetDrawtoField(DRAW_BUFFERED);
2153
2154     BlitScreenToBitmap(backbuffer);
2155
2156     SetDrawtoField(DRAW_BACKBUFFER);
2157
2158     for (yy = 0; yy < ysize; yy++)
2159       for (xx = 0; xx < xsize; xx++)
2160         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2161
2162     DrawTextBuffer(sx + font_width, sy + font_height,
2163                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2164                    xsize - 2, ysize - 2, 0, mask_mode,
2165                    level.envelope[envelope_nr].autowrap,
2166                    level.envelope[envelope_nr].centered, FALSE);
2167
2168     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2169     BackToFront();
2170
2171     WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2172   }
2173 }
2174
2175 void ShowEnvelope(int envelope_nr)
2176 {
2177   int element = EL_ENVELOPE_1 + envelope_nr;
2178   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2179   int sound_opening = element_info[element].sound[ACTION_OPENING];
2180   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2181   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2182   boolean no_delay = (tape.warp_forward);
2183   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2184   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2185   int anim_mode = graphic_info[graphic].anim_mode;
2186   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2187                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2188
2189   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2190
2191   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2192
2193   if (anim_mode == ANIM_DEFAULT)
2194     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2195
2196   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2197
2198   if (tape.playing)
2199     Delay(wait_delay_value);
2200   else
2201     WaitForEventToContinue();
2202
2203   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2204
2205   if (anim_mode != ANIM_NONE)
2206     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2207
2208   if (anim_mode == ANIM_DEFAULT)
2209     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2210
2211   game.envelope_active = FALSE;
2212
2213   SetDrawtoField(DRAW_BUFFERED);
2214
2215   redraw_mask |= REDRAW_FIELD;
2216   BackToFront();
2217 }
2218
2219 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2220 {
2221   int border_size = request.border_size;
2222   int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2223   int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2224   int sx = sx_center - request.width  / 2;
2225   int sy = sy_center - request.height / 2;
2226
2227   if (add_border_size)
2228   {
2229     sx += border_size;
2230     sy += border_size;
2231   }
2232
2233   *x = sx;
2234   *y = sy;
2235 }
2236
2237 void DrawEnvelopeRequest(char *text)
2238 {
2239   char *text_final = text;
2240   char *text_door_style = NULL;
2241   int graphic = IMG_BACKGROUND_REQUEST;
2242   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2243   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2244   int font_nr = FONT_REQUEST;
2245   int font_width = getFontWidth(font_nr);
2246   int font_height = getFontHeight(font_nr);
2247   int border_size = request.border_size;
2248   int line_spacing = request.line_spacing;
2249   int line_height = font_height + line_spacing;
2250   int text_width = request.width - 2 * border_size;
2251   int text_height = request.height - 2 * border_size;
2252   int line_length = text_width / font_width;
2253   int max_lines = text_height / line_height;
2254   int width = request.width;
2255   int height = request.height;
2256   int tile_size = request.step_offset;
2257   int x_steps = width  / tile_size;
2258   int y_steps = height / tile_size;
2259   int sx, sy;
2260   int i, x, y;
2261
2262   if (request.wrap_single_words)
2263   {
2264     char *src_text_ptr, *dst_text_ptr;
2265
2266     text_door_style = checked_malloc(2 * strlen(text) + 1);
2267
2268     src_text_ptr = text;
2269     dst_text_ptr = text_door_style;
2270
2271     while (*src_text_ptr)
2272     {
2273       if (*src_text_ptr == ' ' ||
2274           *src_text_ptr == '?' ||
2275           *src_text_ptr == '!')
2276         *dst_text_ptr++ = '\n';
2277
2278       if (*src_text_ptr != ' ')
2279         *dst_text_ptr++ = *src_text_ptr;
2280
2281       src_text_ptr++;
2282     }
2283
2284     *dst_text_ptr = '\0';
2285
2286     text_final = text_door_style;
2287   }
2288
2289   setRequestPosition(&sx, &sy, FALSE);
2290
2291   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2292
2293   for (y = 0; y < y_steps; y++)
2294     for (x = 0; x < x_steps; x++)
2295       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2296                                   x, y, x_steps, y_steps,
2297                                   tile_size, tile_size);
2298
2299   DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2300                  line_length, -1, max_lines, line_spacing, mask_mode,
2301                  request.autowrap, request.centered, FALSE);
2302
2303   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2304     RedrawGadget(tool_gadget[i]);
2305
2306   // store readily prepared envelope request for later use when animating
2307   BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2308
2309   if (text_door_style)
2310     free(text_door_style);
2311 }
2312
2313 void AnimateEnvelopeRequest(int anim_mode, int action)
2314 {
2315   int graphic = IMG_BACKGROUND_REQUEST;
2316   boolean draw_masked = graphic_info[graphic].draw_masked;
2317   int delay_value_normal = request.step_delay;
2318   int delay_value_fast = delay_value_normal / 2;
2319   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2320   boolean no_delay = (tape.warp_forward);
2321   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2322   int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0);
2323   unsigned int anim_delay = 0;
2324
2325   int width = request.width;
2326   int height = request.height;
2327   int tile_size = request.step_offset;
2328   int max_xsize = width  / tile_size;
2329   int max_ysize = height / tile_size;
2330   int max_xsize_inner = max_xsize - 2;
2331   int max_ysize_inner = max_ysize - 2;
2332
2333   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2334   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2335   int xend = max_xsize_inner;
2336   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2337   int xstep = (xstart < xend ? 1 : 0);
2338   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2339   int x, y;
2340
2341   if (setup.quick_doors)
2342   {
2343     xstart = xend;
2344     ystart = yend;
2345   }
2346   else
2347   {
2348     if (action == ACTION_OPENING)
2349       PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2350     else if (action == ACTION_CLOSING)
2351       PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2352   }
2353
2354   for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2355   {
2356     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2357     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2358     int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2359     int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2360     int src_x = sx_center - width  / 2;
2361     int src_y = sy_center - height / 2;
2362     int dst_x = sx_center - xsize * tile_size / 2;
2363     int dst_y = sy_center - ysize * tile_size / 2;
2364     int xsize_size_left = (xsize - 1) * tile_size;
2365     int ysize_size_top  = (ysize - 1) * tile_size;
2366     int max_xsize_pos = (max_xsize - 1) * tile_size;
2367     int max_ysize_pos = (max_ysize - 1) * tile_size;
2368     int xx, yy;
2369
2370     BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2371
2372     for (yy = 0; yy < 2; yy++)
2373     {
2374       for (xx = 0; xx < 2; xx++)
2375       {
2376         int src_xx = src_x + xx * max_xsize_pos;
2377         int src_yy = src_y + yy * max_ysize_pos;
2378         int dst_xx = dst_x + xx * xsize_size_left;
2379         int dst_yy = dst_y + yy * ysize_size_top;
2380         int xx_size = (xx ? tile_size : xsize_size_left);
2381         int yy_size = (yy ? tile_size : ysize_size_top);
2382
2383         if (draw_masked)
2384           BlitBitmapMasked(bitmap_db_cross, backbuffer,
2385                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2386         else
2387           BlitBitmap(bitmap_db_cross, backbuffer,
2388                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2389       }
2390     }
2391
2392     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2393
2394     DoAnimation();
2395     BackToFront();
2396
2397     WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2398   }
2399 }
2400
2401
2402 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2403 {
2404   int last_game_status = game_status;   /* save current game status */
2405   int graphic = IMG_BACKGROUND_REQUEST;
2406   int sound_opening = SND_REQUEST_OPENING;
2407   int sound_closing = SND_REQUEST_CLOSING;
2408   int anim_mode = graphic_info[graphic].anim_mode;
2409   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2410                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2411
2412   if (game_status == GAME_MODE_PLAYING)
2413     BlitScreenToBitmap(backbuffer);
2414
2415   SetDrawtoField(DRAW_BACKBUFFER);
2416
2417   // SetDrawBackgroundMask(REDRAW_NONE);
2418
2419   if (action == ACTION_OPENING)
2420   {
2421     BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2422
2423     if (req_state & REQ_ASK)
2424     {
2425       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2426       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2427     }
2428     else if (req_state & REQ_CONFIRM)
2429     {
2430       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2431     }
2432     else if (req_state & REQ_PLAYER)
2433     {
2434       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2435       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2436       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2437       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2438     }
2439
2440     DrawEnvelopeRequest(text);
2441
2442     if (game_status != GAME_MODE_MAIN)
2443       InitAnimation();
2444   }
2445
2446   /* force DOOR font inside door area */
2447   game_status = GAME_MODE_PSEUDO_DOOR;
2448
2449   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2450
2451   if (action == ACTION_OPENING)
2452   {
2453     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2454
2455     if (anim_mode == ANIM_DEFAULT)
2456       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2457
2458     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2459
2460   }
2461   else
2462   {
2463     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2464
2465     if (anim_mode != ANIM_NONE)
2466       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2467
2468     if (anim_mode == ANIM_DEFAULT)
2469       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2470   }
2471
2472   game.envelope_active = FALSE;
2473
2474   game_status = last_game_status;       /* restore current game status */
2475
2476   if (action == ACTION_CLOSING)
2477   {
2478     if (game_status != GAME_MODE_MAIN)
2479       StopAnimation();
2480
2481     BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2482   }
2483
2484   // SetDrawBackgroundMask(last_draw_background_mask);
2485
2486   redraw_mask |= REDRAW_FIELD;
2487
2488   if (game_status == GAME_MODE_MAIN)
2489     DoAnimation();
2490
2491   BackToFront();
2492
2493   if (action == ACTION_CLOSING &&
2494       game_status == GAME_MODE_PLAYING &&
2495       level.game_engine_type == GAME_ENGINE_TYPE_RND)
2496     SetDrawtoField(DRAW_BUFFERED);
2497 }
2498
2499 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2500 {
2501   Bitmap *src_bitmap;
2502   int src_x, src_y;
2503   int graphic = el2preimg(element);
2504
2505   getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2506   BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2507 }
2508
2509 void DrawLevel(int draw_background_mask)
2510 {
2511   int x,y;
2512
2513   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2514   SetDrawBackgroundMask(draw_background_mask);
2515
2516   ClearField();
2517
2518   for (x = BX1; x <= BX2; x++)
2519     for (y = BY1; y <= BY2; y++)
2520       DrawScreenField(x, y);
2521
2522   redraw_mask |= REDRAW_FIELD;
2523 }
2524
2525 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2526                     int tilesize)
2527 {
2528   int x,y;
2529
2530   for (x = 0; x < size_x; x++)
2531     for (y = 0; y < size_y; y++)
2532       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2533
2534   redraw_mask |= REDRAW_FIELD;
2535 }
2536
2537 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2538 {
2539   int x,y;
2540
2541   for (x = 0; x < size_x; x++)
2542     for (y = 0; y < size_y; y++)
2543       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2544
2545   redraw_mask |= REDRAW_FIELD;
2546 }
2547
2548 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2549 {
2550   boolean show_level_border = (BorderElement != EL_EMPTY);
2551   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2552   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2553   int tile_size = preview.tile_size;
2554   int preview_width  = preview.xsize * tile_size;
2555   int preview_height = preview.ysize * tile_size;
2556   int real_preview_xsize = MIN(level_xsize, preview.xsize);
2557   int real_preview_ysize = MIN(level_ysize, preview.ysize);
2558   int real_preview_width  = real_preview_xsize * tile_size;
2559   int real_preview_height = real_preview_ysize * tile_size;
2560   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2561   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2562   int x, y;
2563
2564   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2565     return;
2566
2567   DrawBackground(dst_x, dst_y, preview_width, preview_height);
2568
2569   dst_x += (preview_width  - real_preview_width)  / 2;
2570   dst_y += (preview_height - real_preview_height) / 2;
2571
2572   for (x = 0; x < real_preview_xsize; x++)
2573   {
2574     for (y = 0; y < real_preview_ysize; y++)
2575     {
2576       int lx = from_x + x + (show_level_border ? -1 : 0);
2577       int ly = from_y + y + (show_level_border ? -1 : 0);
2578       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2579                      getBorderElement(lx, ly));
2580
2581       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2582                          element, tile_size);
2583     }
2584   }
2585
2586   redraw_mask |= REDRAW_MICROLEVEL;
2587 }
2588
2589 #define MICROLABEL_EMPTY                0
2590 #define MICROLABEL_LEVEL_NAME           1
2591 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
2592 #define MICROLABEL_LEVEL_AUTHOR         3
2593 #define MICROLABEL_IMPORTED_FROM_HEAD   4
2594 #define MICROLABEL_IMPORTED_FROM        5
2595 #define MICROLABEL_IMPORTED_BY_HEAD     6
2596 #define MICROLABEL_IMPORTED_BY          7
2597
2598 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2599 {
2600   int max_text_width = SXSIZE;
2601   int font_width = getFontWidth(font_nr);
2602
2603   if (pos->align == ALIGN_CENTER)
2604     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2605   else if (pos->align == ALIGN_RIGHT)
2606     max_text_width = pos->x;
2607   else
2608     max_text_width = SXSIZE - pos->x;
2609
2610   return max_text_width / font_width;
2611 }
2612
2613 static void DrawPreviewLevelLabelExt(int mode)
2614 {
2615   struct TextPosInfo *pos = &menu.main.text.level_info_2;
2616   char label_text[MAX_OUTPUT_LINESIZE + 1];
2617   int max_len_label_text;
2618   int font_nr = pos->font;
2619   int i;
2620
2621   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2622     return;
2623
2624   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2625       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2626       mode == MICROLABEL_IMPORTED_BY_HEAD)
2627     font_nr = pos->font_alt;
2628
2629   max_len_label_text = getMaxTextLength(pos, font_nr);
2630
2631   if (pos->size != -1)
2632     max_len_label_text = pos->size;
2633
2634   for (i = 0; i < max_len_label_text; i++)
2635     label_text[i] = ' ';
2636   label_text[max_len_label_text] = '\0';
2637
2638   if (strlen(label_text) > 0)
2639     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2640
2641   strncpy(label_text,
2642           (mode == MICROLABEL_LEVEL_NAME ? level.name :
2643            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2644            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2645            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2646            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2647            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2648            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2649           max_len_label_text);
2650   label_text[max_len_label_text] = '\0';
2651
2652   if (strlen(label_text) > 0)
2653     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2654
2655   redraw_mask |= REDRAW_MICROLEVEL;
2656 }
2657
2658 static void DrawPreviewLevelExt(boolean restart)
2659 {
2660   static unsigned int scroll_delay = 0;
2661   static unsigned int label_delay = 0;
2662   static int from_x, from_y, scroll_direction;
2663   static int label_state, label_counter;
2664   unsigned int scroll_delay_value = preview.step_delay;
2665   boolean show_level_border = (BorderElement != EL_EMPTY);
2666   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2667   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2668   int last_game_status = game_status;           /* save current game status */
2669
2670   if (restart)
2671   {
2672     from_x = 0;
2673     from_y = 0;
2674
2675     if (preview.anim_mode == ANIM_CENTERED)
2676     {
2677       if (level_xsize > preview.xsize)
2678         from_x = (level_xsize - preview.xsize) / 2;
2679       if (level_ysize > preview.ysize)
2680         from_y = (level_ysize - preview.ysize) / 2;
2681     }
2682
2683     from_x += preview.xoffset;
2684     from_y += preview.yoffset;
2685
2686     scroll_direction = MV_RIGHT;
2687     label_state = 1;
2688     label_counter = 0;
2689
2690     DrawPreviewLevelPlayfieldExt(from_x, from_y);
2691     DrawPreviewLevelLabelExt(label_state);
2692
2693     /* initialize delay counters */
2694     DelayReached(&scroll_delay, 0);
2695     DelayReached(&label_delay, 0);
2696
2697     if (leveldir_current->name)
2698     {
2699       struct TextPosInfo *pos = &menu.main.text.level_info_1;
2700       char label_text[MAX_OUTPUT_LINESIZE + 1];
2701       int font_nr = pos->font;
2702       int max_len_label_text = getMaxTextLength(pos, font_nr);
2703
2704       if (pos->size != -1)
2705         max_len_label_text = pos->size;
2706
2707       strncpy(label_text, leveldir_current->name, max_len_label_text);
2708       label_text[max_len_label_text] = '\0';
2709
2710       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2711         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2712     }
2713
2714     game_status = last_game_status;     /* restore current game status */
2715
2716     return;
2717   }
2718
2719   /* scroll preview level, if needed */
2720   if (preview.anim_mode != ANIM_NONE &&
2721       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2722       DelayReached(&scroll_delay, scroll_delay_value))
2723   {
2724     switch (scroll_direction)
2725     {
2726       case MV_LEFT:
2727         if (from_x > 0)
2728         {
2729           from_x -= preview.step_offset;
2730           from_x = (from_x < 0 ? 0 : from_x);
2731         }
2732         else
2733           scroll_direction = MV_UP;
2734         break;
2735
2736       case MV_RIGHT:
2737         if (from_x < level_xsize - preview.xsize)
2738         {
2739           from_x += preview.step_offset;
2740           from_x = (from_x > level_xsize - preview.xsize ?
2741                     level_xsize - preview.xsize : from_x);
2742         }
2743         else
2744           scroll_direction = MV_DOWN;
2745         break;
2746
2747       case MV_UP:
2748         if (from_y > 0)
2749         {
2750           from_y -= preview.step_offset;
2751           from_y = (from_y < 0 ? 0 : from_y);
2752         }
2753         else
2754           scroll_direction = MV_RIGHT;
2755         break;
2756
2757       case MV_DOWN:
2758         if (from_y < level_ysize - preview.ysize)
2759         {
2760           from_y += preview.step_offset;
2761           from_y = (from_y > level_ysize - preview.ysize ?
2762                     level_ysize - preview.ysize : from_y);
2763         }
2764         else
2765           scroll_direction = MV_LEFT;
2766         break;
2767
2768       default:
2769         break;
2770     }
2771
2772     DrawPreviewLevelPlayfieldExt(from_x, from_y);
2773   }
2774
2775   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2776   /* redraw micro level label, if needed */
2777   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2778       !strEqual(level.author, ANONYMOUS_NAME) &&
2779       !strEqual(level.author, leveldir_current->name) &&
2780       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2781   {
2782     int max_label_counter = 23;
2783
2784     if (leveldir_current->imported_from != NULL &&
2785         strlen(leveldir_current->imported_from) > 0)
2786       max_label_counter += 14;
2787     if (leveldir_current->imported_by != NULL &&
2788         strlen(leveldir_current->imported_by) > 0)
2789       max_label_counter += 14;
2790
2791     label_counter = (label_counter + 1) % max_label_counter;
2792     label_state = (label_counter >= 0 && label_counter <= 7 ?
2793                    MICROLABEL_LEVEL_NAME :
2794                    label_counter >= 9 && label_counter <= 12 ?
2795                    MICROLABEL_LEVEL_AUTHOR_HEAD :
2796                    label_counter >= 14 && label_counter <= 21 ?
2797                    MICROLABEL_LEVEL_AUTHOR :
2798                    label_counter >= 23 && label_counter <= 26 ?
2799                    MICROLABEL_IMPORTED_FROM_HEAD :
2800                    label_counter >= 28 && label_counter <= 35 ?
2801                    MICROLABEL_IMPORTED_FROM :
2802                    label_counter >= 37 && label_counter <= 40 ?
2803                    MICROLABEL_IMPORTED_BY_HEAD :
2804                    label_counter >= 42 && label_counter <= 49 ?
2805                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2806
2807     if (leveldir_current->imported_from == NULL &&
2808         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2809          label_state == MICROLABEL_IMPORTED_FROM))
2810       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2811                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2812
2813     DrawPreviewLevelLabelExt(label_state);
2814   }
2815
2816   game_status = last_game_status;       /* restore current game status */
2817 }
2818
2819 void DrawPreviewLevelInitial()
2820 {
2821   DrawPreviewLevelExt(TRUE);
2822 }
2823
2824 void DrawPreviewLevelAnimation()
2825 {
2826   DrawPreviewLevelExt(FALSE);
2827 }
2828
2829 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2830                                     int graphic, int sync_frame, int mask_mode)
2831 {
2832   int frame = getGraphicAnimationFrame(graphic, sync_frame);
2833
2834   if (mask_mode == USE_MASKING)
2835     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2836   else
2837     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2838 }
2839
2840 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2841                                          int graphic, int sync_frame,
2842                                          int mask_mode)
2843 {
2844   int frame = getGraphicAnimationFrame(graphic, sync_frame);
2845
2846   if (mask_mode == USE_MASKING)
2847     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2848   else
2849     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2850 }
2851
2852 inline void DrawGraphicAnimation(int x, int y, int graphic)
2853 {
2854   int lx = LEVELX(x), ly = LEVELY(y);
2855
2856   if (!IN_SCR_FIELD(x, y))
2857     return;
2858
2859   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2860                           graphic, GfxFrame[lx][ly], NO_MASKING);
2861
2862   MarkTileDirty(x, y);
2863 }
2864
2865 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2866 {
2867   int lx = LEVELX(x), ly = LEVELY(y);
2868
2869   if (!IN_SCR_FIELD(x, y))
2870     return;
2871
2872   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2873                           graphic, GfxFrame[lx][ly], NO_MASKING);
2874   MarkTileDirty(x, y);
2875 }
2876
2877 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2878 {
2879   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2880 }
2881
2882 void DrawLevelElementAnimation(int x, int y, int element)
2883 {
2884   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2885
2886   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2887 }
2888
2889 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2890 {
2891   int sx = SCREENX(x), sy = SCREENY(y);
2892
2893   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2894     return;
2895
2896   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2897     return;
2898
2899   DrawGraphicAnimation(sx, sy, graphic);
2900
2901 #if 1
2902   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2903     DrawLevelFieldCrumbled(x, y);
2904 #else
2905   if (GFX_CRUMBLED(Feld[x][y]))
2906     DrawLevelFieldCrumbled(x, y);
2907 #endif
2908 }
2909
2910 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2911 {
2912   int sx = SCREENX(x), sy = SCREENY(y);
2913   int graphic;
2914
2915   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2916     return;
2917
2918   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2919
2920   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2921     return;
2922
2923   DrawGraphicAnimation(sx, sy, graphic);
2924
2925   if (GFX_CRUMBLED(element))
2926     DrawLevelFieldCrumbled(x, y);
2927 }
2928
2929 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2930 {
2931   if (player->use_murphy)
2932   {
2933     /* this works only because currently only one player can be "murphy" ... */
2934     static int last_horizontal_dir = MV_LEFT;
2935     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2936
2937     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2938       last_horizontal_dir = move_dir;
2939
2940     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
2941     {
2942       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2943
2944       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2945     }
2946
2947     return graphic;
2948   }
2949   else
2950     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2951 }
2952
2953 static boolean equalGraphics(int graphic1, int graphic2)
2954 {
2955   struct GraphicInfo *g1 = &graphic_info[graphic1];
2956   struct GraphicInfo *g2 = &graphic_info[graphic2];
2957
2958   return (g1->bitmap      == g2->bitmap &&
2959           g1->src_x       == g2->src_x &&
2960           g1->src_y       == g2->src_y &&
2961           g1->anim_frames == g2->anim_frames &&
2962           g1->anim_delay  == g2->anim_delay &&
2963           g1->anim_mode   == g2->anim_mode);
2964 }
2965
2966 void DrawAllPlayers()
2967 {
2968   int i;
2969
2970   for (i = 0; i < MAX_PLAYERS; i++)
2971     if (stored_player[i].active)
2972       DrawPlayer(&stored_player[i]);
2973 }
2974
2975 void DrawPlayerField(int x, int y)
2976 {
2977   if (!IS_PLAYER(x, y))
2978     return;
2979
2980   DrawPlayer(PLAYERINFO(x, y));
2981 }
2982
2983 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2984
2985 void DrawPlayer(struct PlayerInfo *player)
2986 {
2987   int jx = player->jx;
2988   int jy = player->jy;
2989   int move_dir = player->MovDir;
2990   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2991   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
2992   int last_jx = (player->is_moving ? jx - dx : jx);
2993   int last_jy = (player->is_moving ? jy - dy : jy);
2994   int next_jx = jx + dx;
2995   int next_jy = jy + dy;
2996   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
2997   boolean player_is_opaque = FALSE;
2998   int sx = SCREENX(jx), sy = SCREENY(jy);
2999   int sxx = 0, syy = 0;
3000   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3001   int graphic;
3002   int action = ACTION_DEFAULT;
3003   int last_player_graphic = getPlayerGraphic(player, move_dir);
3004   int last_player_frame = player->Frame;
3005   int frame = 0;
3006
3007   /* GfxElement[][] is set to the element the player is digging or collecting;
3008      remove also for off-screen player if the player is not moving anymore */
3009   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3010     GfxElement[jx][jy] = EL_UNDEFINED;
3011
3012   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3013     return;
3014
3015 #if DEBUG
3016   if (!IN_LEV_FIELD(jx, jy))
3017   {
3018     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3019     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3020     printf("DrawPlayerField(): This should never happen!\n");
3021     return;
3022   }
3023 #endif
3024
3025   if (element == EL_EXPLOSION)
3026     return;
3027
3028   action = (player->is_pushing    ? ACTION_PUSHING         :
3029             player->is_digging    ? ACTION_DIGGING         :
3030             player->is_collecting ? ACTION_COLLECTING      :
3031             player->is_moving     ? ACTION_MOVING          :
3032             player->is_snapping   ? ACTION_SNAPPING        :
3033             player->is_dropping   ? ACTION_DROPPING        :
3034             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
3035
3036   if (player->is_waiting)
3037     move_dir = player->dir_waiting;
3038
3039   InitPlayerGfxAnimation(player, action, move_dir);
3040
3041   /* ----------------------------------------------------------------------- */
3042   /* draw things in the field the player is leaving, if needed               */
3043   /* ----------------------------------------------------------------------- */
3044
3045   if (player->is_moving)
3046   {
3047     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3048     {
3049       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3050
3051       if (last_element == EL_DYNAMITE_ACTIVE ||
3052           last_element == EL_EM_DYNAMITE_ACTIVE ||
3053           last_element == EL_SP_DISK_RED_ACTIVE)
3054         DrawDynamite(last_jx, last_jy);
3055       else
3056         DrawLevelFieldThruMask(last_jx, last_jy);
3057     }
3058     else if (last_element == EL_DYNAMITE_ACTIVE ||
3059              last_element == EL_EM_DYNAMITE_ACTIVE ||
3060              last_element == EL_SP_DISK_RED_ACTIVE)
3061       DrawDynamite(last_jx, last_jy);
3062 #if 0
3063     /* !!! this is not enough to prevent flickering of players which are
3064        moving next to each others without a free tile between them -- this
3065        can only be solved by drawing all players layer by layer (first the
3066        background, then the foreground etc.) !!! => TODO */
3067     else if (!IS_PLAYER(last_jx, last_jy))
3068       DrawLevelField(last_jx, last_jy);
3069 #else
3070     else
3071       DrawLevelField(last_jx, last_jy);
3072 #endif
3073
3074     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3075       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3076   }
3077
3078   if (!IN_SCR_FIELD(sx, sy))
3079     return;
3080
3081   /* ----------------------------------------------------------------------- */
3082   /* draw things behind the player, if needed                                */
3083   /* ----------------------------------------------------------------------- */
3084
3085   if (Back[jx][jy])
3086     DrawLevelElement(jx, jy, Back[jx][jy]);
3087   else if (IS_ACTIVE_BOMB(element))
3088     DrawLevelElement(jx, jy, EL_EMPTY);
3089   else
3090   {
3091     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3092     {
3093       int old_element = GfxElement[jx][jy];
3094       int old_graphic = el_act_dir2img(old_element, action, move_dir);
3095       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3096
3097       if (GFX_CRUMBLED(old_element))
3098         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3099       else
3100         DrawGraphic(sx, sy, old_graphic, frame);
3101
3102       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3103         player_is_opaque = TRUE;
3104     }
3105     else
3106     {
3107       GfxElement[jx][jy] = EL_UNDEFINED;
3108
3109       /* make sure that pushed elements are drawn with correct frame rate */
3110       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3111
3112       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3113         GfxFrame[jx][jy] = player->StepFrame;
3114
3115       DrawLevelField(jx, jy);
3116     }
3117   }
3118
3119 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3120   /* ----------------------------------------------------------------------- */
3121   /* draw player himself                                                     */
3122   /* ----------------------------------------------------------------------- */
3123
3124   graphic = getPlayerGraphic(player, move_dir);
3125
3126   /* in the case of changed player action or direction, prevent the current
3127      animation frame from being restarted for identical animations */
3128   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3129     player->Frame = last_player_frame;
3130
3131   frame = getGraphicAnimationFrame(graphic, player->Frame);
3132
3133   if (player->GfxPos)
3134   {
3135     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3136       sxx = player->GfxPos;
3137     else
3138       syy = player->GfxPos;
3139   }
3140
3141   if (!setup.soft_scrolling && ScreenMovPos)
3142     sxx = syy = 0;
3143
3144   if (player_is_opaque)
3145     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3146   else
3147     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3148
3149   if (SHIELD_ON(player))
3150   {
3151     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3152                    IMG_SHIELD_NORMAL_ACTIVE);
3153     int frame = getGraphicAnimationFrame(graphic, -1);
3154
3155     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3156   }
3157 #endif
3158
3159 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3160   if (player->GfxPos)
3161   {
3162     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3163       sxx = player->GfxPos;
3164     else
3165       syy = player->GfxPos;
3166   }
3167 #endif
3168
3169   /* ----------------------------------------------------------------------- */
3170   /* draw things the player is pushing, if needed                            */
3171   /* ----------------------------------------------------------------------- */
3172
3173   if (player->is_pushing && player->is_moving)
3174   {
3175     int px = SCREENX(jx), py = SCREENY(jy);
3176     int pxx = (TILEX - ABS(sxx)) * dx;
3177     int pyy = (TILEY - ABS(syy)) * dy;
3178     int gfx_frame = GfxFrame[jx][jy];
3179
3180     int graphic;
3181     int sync_frame;
3182     int frame;
3183
3184     if (!IS_MOVING(jx, jy))             /* push movement already finished */
3185     {
3186       element = Feld[next_jx][next_jy];
3187       gfx_frame = GfxFrame[next_jx][next_jy];
3188     }
3189
3190     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3191
3192     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3193     frame = getGraphicAnimationFrame(graphic, sync_frame);
3194
3195     /* draw background element under pushed element (like the Sokoban field) */
3196     if (game.use_masked_pushing && IS_MOVING(jx, jy))
3197     {
3198       /* this allows transparent pushing animation over non-black background */
3199
3200       if (Back[jx][jy])
3201         DrawLevelElement(jx, jy, Back[jx][jy]);
3202       else
3203         DrawLevelElement(jx, jy, EL_EMPTY);
3204
3205       if (Back[next_jx][next_jy])
3206         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3207       else
3208         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3209     }
3210     else if (Back[next_jx][next_jy])
3211       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3212
3213 #if 1
3214     /* do not draw (EM style) pushing animation when pushing is finished */
3215     /* (two-tile animations usually do not contain start and end frame) */
3216     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3217       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3218     else
3219       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3220 #else
3221     /* masked drawing is needed for EMC style (double) movement graphics */
3222     /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3223     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3224 #endif
3225   }
3226
3227 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3228   /* ----------------------------------------------------------------------- */
3229   /* draw player himself                                                     */
3230   /* ----------------------------------------------------------------------- */
3231
3232   graphic = getPlayerGraphic(player, move_dir);
3233
3234   /* in the case of changed player action or direction, prevent the current
3235      animation frame from being restarted for identical animations */
3236   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3237     player->Frame = last_player_frame;
3238
3239   frame = getGraphicAnimationFrame(graphic, player->Frame);
3240
3241   if (player->GfxPos)
3242   {
3243     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3244       sxx = player->GfxPos;
3245     else
3246       syy = player->GfxPos;
3247   }
3248
3249   if (!setup.soft_scrolling && ScreenMovPos)
3250     sxx = syy = 0;
3251
3252   if (player_is_opaque)
3253     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3254   else
3255     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3256
3257   if (SHIELD_ON(player))
3258   {
3259     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3260                    IMG_SHIELD_NORMAL_ACTIVE);
3261     int frame = getGraphicAnimationFrame(graphic, -1);
3262
3263     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3264   }
3265 #endif
3266
3267   /* ----------------------------------------------------------------------- */
3268   /* draw things in front of player (active dynamite or dynabombs)           */
3269   /* ----------------------------------------------------------------------- */
3270
3271   if (IS_ACTIVE_BOMB(element))
3272   {
3273     graphic = el2img(element);
3274     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3275
3276     if (game.emulation == EMU_SUPAPLEX)
3277       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3278     else
3279       DrawGraphicThruMask(sx, sy, graphic, frame);
3280   }
3281
3282   if (player_is_moving && last_element == EL_EXPLOSION)
3283   {
3284     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3285                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
3286     int graphic = el_act2img(element, ACTION_EXPLODING);
3287     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3288     int phase = ExplodePhase[last_jx][last_jy] - 1;
3289     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3290
3291     if (phase >= delay)
3292       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3293   }
3294
3295   /* ----------------------------------------------------------------------- */
3296   /* draw elements the player is just walking/passing through/under          */
3297   /* ----------------------------------------------------------------------- */
3298
3299   if (player_is_moving)
3300   {
3301     /* handle the field the player is leaving ... */
3302     if (IS_ACCESSIBLE_INSIDE(last_element))
3303       DrawLevelField(last_jx, last_jy);
3304     else if (IS_ACCESSIBLE_UNDER(last_element))
3305       DrawLevelFieldThruMask(last_jx, last_jy);
3306   }
3307
3308   /* do not redraw accessible elements if the player is just pushing them */
3309   if (!player_is_moving || !player->is_pushing)
3310   {
3311     /* ... and the field the player is entering */
3312     if (IS_ACCESSIBLE_INSIDE(element))
3313       DrawLevelField(jx, jy);
3314     else if (IS_ACCESSIBLE_UNDER(element))
3315       DrawLevelFieldThruMask(jx, jy);
3316   }
3317
3318   MarkTileDirty(sx, sy);
3319 }
3320
3321 /* ------------------------------------------------------------------------- */
3322
3323 void WaitForEventToContinue()
3324 {
3325   boolean still_wait = TRUE;
3326
3327   /* simulate releasing mouse button over last gadget, if still pressed */
3328   if (button_status)
3329     HandleGadgets(-1, -1, 0);
3330
3331   button_status = MB_RELEASED;
3332
3333   ClearEventQueue();
3334
3335   while (still_wait)
3336   {
3337     if (PendingEvent())
3338     {
3339       Event event;
3340
3341       NextEvent(&event);
3342
3343       switch (event.type)
3344       {
3345         case EVENT_BUTTONPRESS:
3346         case EVENT_KEYPRESS:
3347           still_wait = FALSE;
3348           break;
3349
3350         case EVENT_KEYRELEASE:
3351           ClearPlayerAction();
3352           break;
3353
3354         default:
3355           HandleOtherEvents(&event);
3356           break;
3357       }
3358     }
3359     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3360     {
3361       still_wait = FALSE;
3362     }
3363
3364     DoAnimation();
3365
3366     /* don't eat all CPU time */
3367     Delay(10);
3368   }
3369 }
3370
3371 #define MAX_REQUEST_LINES               13
3372 #define MAX_REQUEST_LINE_FONT1_LEN      7
3373 #define MAX_REQUEST_LINE_FONT2_LEN      10
3374
3375 static int RequestHandleEvents(unsigned int req_state)
3376 {
3377   int last_game_status = game_status;   /* save current game status */
3378   int result;
3379   int mx, my;
3380
3381   button_status = MB_RELEASED;
3382
3383   request_gadget_id = -1;
3384   result = -1;
3385
3386   while (result < 0)
3387   {
3388     if (PendingEvent())
3389     {
3390       Event event;
3391
3392       while (NextValidEvent(&event))
3393       {
3394         switch (event.type)
3395         {
3396           case EVENT_BUTTONPRESS:
3397           case EVENT_BUTTONRELEASE:
3398           case EVENT_MOTIONNOTIFY:
3399           {
3400             if (event.type == EVENT_MOTIONNOTIFY)
3401             {
3402               if (!PointerInWindow(window))
3403                 continue;       /* window and pointer on different screens */
3404
3405               if (!button_status)
3406                 continue;
3407
3408               motion_status = TRUE;
3409               mx = ((MotionEvent *) &event)->x;
3410               my = ((MotionEvent *) &event)->y;
3411             }
3412             else
3413             {
3414               motion_status = FALSE;
3415               mx = ((ButtonEvent *) &event)->x;
3416               my = ((ButtonEvent *) &event)->y;
3417               if (event.type == EVENT_BUTTONPRESS)
3418                 button_status = ((ButtonEvent *) &event)->button;
3419               else
3420                 button_status = MB_RELEASED;
3421             }
3422
3423             /* this sets 'request_gadget_id' */
3424             HandleGadgets(mx, my, button_status);
3425
3426             switch (request_gadget_id)
3427             {
3428               case TOOL_CTRL_ID_YES:
3429                 result = TRUE;
3430                 break;
3431               case TOOL_CTRL_ID_NO:
3432                 result = FALSE;
3433                 break;
3434               case TOOL_CTRL_ID_CONFIRM:
3435                 result = TRUE | FALSE;
3436                 break;
3437
3438               case TOOL_CTRL_ID_PLAYER_1:
3439                 result = 1;
3440                 break;
3441               case TOOL_CTRL_ID_PLAYER_2:
3442                 result = 2;
3443                 break;
3444               case TOOL_CTRL_ID_PLAYER_3:
3445                 result = 3;
3446                 break;
3447               case TOOL_CTRL_ID_PLAYER_4:
3448                 result = 4;
3449                 break;
3450
3451               default:
3452                 break;
3453             }
3454
3455             break;
3456           }
3457
3458           case EVENT_KEYPRESS:
3459             switch (GetEventKey((KeyEvent *)&event, TRUE))
3460             {
3461               case KSYM_space:
3462                 if (req_state & REQ_CONFIRM)
3463                   result = 1;
3464                 break;
3465
3466               case KSYM_Return:
3467 #if defined(TARGET_SDL2)
3468               case KSYM_Menu:
3469 #endif
3470                 result = 1;
3471                 break;
3472
3473               case KSYM_Escape:
3474 #if defined(TARGET_SDL2)
3475               case KSYM_Back:
3476 #endif
3477                 result = 0;
3478                 break;
3479
3480               default:
3481                 break;
3482             }
3483
3484             if (req_state & REQ_PLAYER)
3485               result = 0;
3486             break;
3487
3488           case EVENT_KEYRELEASE:
3489             ClearPlayerAction();
3490             break;
3491
3492           default:
3493             HandleOtherEvents(&event);
3494             break;
3495         }
3496       }
3497     }
3498     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3499     {
3500       int joy = AnyJoystick();
3501
3502       if (joy & JOY_BUTTON_1)
3503         result = 1;
3504       else if (joy & JOY_BUTTON_2)
3505         result = 0;
3506     }
3507
3508     if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3509     {
3510       HandleGameActions();
3511     }
3512     else
3513     {
3514       DoAnimation();
3515
3516       if (!PendingEvent())      /* delay only if no pending events */
3517         Delay(10);
3518     }
3519
3520     game_status = GAME_MODE_PSEUDO_DOOR;
3521
3522     BackToFront();
3523
3524     game_status = last_game_status;     /* restore current game status */
3525   }
3526
3527   return result;
3528 }
3529
3530 static boolean RequestDoor(char *text, unsigned int req_state)
3531 {
3532   unsigned int old_door_state;
3533   int last_game_status = game_status;   /* save current game status */
3534   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3535   int font_nr = FONT_TEXT_2;
3536   char *text_ptr;
3537   int result;
3538   int ty;
3539
3540   if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3541   {
3542     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3543     font_nr = FONT_TEXT_1;
3544   }
3545
3546   if (game_status == GAME_MODE_PLAYING)
3547     BlitScreenToBitmap(backbuffer);
3548
3549   /* disable deactivated drawing when quick-loading level tape recording */
3550   if (tape.playing && tape.deactivate_display)
3551     TapeDeactivateDisplayOff(TRUE);
3552
3553   SetMouseCursor(CURSOR_DEFAULT);
3554
3555 #if defined(NETWORK_AVALIABLE)
3556   /* pause network game while waiting for request to answer */
3557   if (options.network &&
3558       game_status == GAME_MODE_PLAYING &&
3559       req_state & REQUEST_WAIT_FOR_INPUT)
3560     SendToServer_PausePlaying();
3561 #endif
3562
3563   old_door_state = GetDoorState();
3564
3565   /* simulate releasing mouse button over last gadget, if still pressed */
3566   if (button_status)
3567     HandleGadgets(-1, -1, 0);
3568
3569   UnmapAllGadgets();
3570
3571   /* draw released gadget before proceeding */
3572   // BackToFront();
3573
3574   if (old_door_state & DOOR_OPEN_1)
3575   {
3576     CloseDoor(DOOR_CLOSE_1);
3577
3578     /* save old door content */
3579     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3580                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3581   }
3582
3583   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3584   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3585
3586   /* clear door drawing field */
3587   DrawBackground(DX, DY, DXSIZE, DYSIZE);
3588
3589   /* force DOOR font inside door area */
3590   game_status = GAME_MODE_PSEUDO_DOOR;
3591
3592   /* write text for request */
3593   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3594   {
3595     char text_line[max_request_line_len + 1];
3596     int tx, tl, tc = 0;
3597
3598     if (!*text_ptr)
3599       break;
3600
3601     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3602     {
3603       tc = *(text_ptr + tx);
3604       // if (!tc || tc == ' ')
3605       if (!tc || tc == ' ' || tc == '?' || tc == '!')
3606         break;
3607     }
3608
3609     if ((tc == '?' || tc == '!') && tl == 0)
3610       tl = 1;
3611
3612     if (!tl)
3613     { 
3614       text_ptr++; 
3615       ty--; 
3616       continue; 
3617     }
3618
3619     strncpy(text_line, text_ptr, tl);
3620     text_line[tl] = 0;
3621
3622     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3623              DY + 8 + ty * (getFontHeight(font_nr) + 2),
3624              text_line, font_nr);
3625
3626     text_ptr += tl + (tc == ' ' ? 1 : 0);
3627     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3628   }
3629
3630   game_status = last_game_status;       /* restore current game status */
3631
3632   if (req_state & REQ_ASK)
3633   {
3634     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3635     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3636   }
3637   else if (req_state & REQ_CONFIRM)
3638   {
3639     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3640   }
3641   else if (req_state & REQ_PLAYER)
3642   {
3643     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3644     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3645     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3646     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3647   }
3648
3649   /* copy request gadgets to door backbuffer */
3650   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3651
3652   OpenDoor(DOOR_OPEN_1);
3653
3654   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3655   {
3656     if (game_status == GAME_MODE_PLAYING)
3657     {
3658       SetPanelBackground();
3659       SetDrawBackgroundMask(REDRAW_DOOR_1);
3660     }
3661     else
3662     {
3663       SetDrawBackgroundMask(REDRAW_FIELD);
3664     }
3665
3666     return FALSE;
3667   }
3668
3669   if (game_status != GAME_MODE_MAIN)
3670     InitAnimation();
3671
3672   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3673
3674   // ---------- handle request buttons ----------
3675   result = RequestHandleEvents(req_state);
3676
3677   if (game_status != GAME_MODE_MAIN)
3678     StopAnimation();
3679
3680   UnmapToolButtons();
3681
3682   if (!(req_state & REQ_STAY_OPEN))
3683   {
3684     CloseDoor(DOOR_CLOSE_1);
3685
3686     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3687         (req_state & REQ_REOPEN))
3688       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3689   }
3690
3691   RemapAllGadgets();
3692
3693   if (game_status == GAME_MODE_PLAYING)
3694   {
3695     SetPanelBackground();
3696     SetDrawBackgroundMask(REDRAW_DOOR_1);
3697   }
3698   else
3699   {
3700     SetDrawBackgroundMask(REDRAW_FIELD);
3701   }
3702
3703 #if defined(NETWORK_AVALIABLE)
3704   /* continue network game after request */
3705   if (options.network &&
3706       game_status == GAME_MODE_PLAYING &&
3707       req_state & REQUEST_WAIT_FOR_INPUT)
3708     SendToServer_ContinuePlaying();
3709 #endif
3710
3711   /* restore deactivated drawing when quick-loading level tape recording */
3712   if (tape.playing && tape.deactivate_display)
3713     TapeDeactivateDisplayOn();
3714
3715   return result;
3716 }
3717
3718 static boolean RequestEnvelope(char *text, unsigned int req_state)
3719 {
3720   int result;
3721
3722   if (game_status == GAME_MODE_PLAYING)
3723     BlitScreenToBitmap(backbuffer);
3724
3725   /* disable deactivated drawing when quick-loading level tape recording */
3726   if (tape.playing && tape.deactivate_display)
3727     TapeDeactivateDisplayOff(TRUE);
3728
3729   SetMouseCursor(CURSOR_DEFAULT);
3730
3731 #if defined(NETWORK_AVALIABLE)
3732   /* pause network game while waiting for request to answer */
3733   if (options.network &&
3734       game_status == GAME_MODE_PLAYING &&
3735       req_state & REQUEST_WAIT_FOR_INPUT)
3736     SendToServer_PausePlaying();
3737 #endif
3738
3739   /* simulate releasing mouse button over last gadget, if still pressed */
3740   if (button_status)
3741     HandleGadgets(-1, -1, 0);
3742
3743   UnmapAllGadgets();
3744
3745   // (replace with setting corresponding request background)
3746   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3747   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3748
3749   /* clear door drawing field */
3750   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3751
3752   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3753
3754   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3755   {
3756     if (game_status == GAME_MODE_PLAYING)
3757     {
3758       SetPanelBackground();
3759       SetDrawBackgroundMask(REDRAW_DOOR_1);
3760     }
3761     else
3762     {
3763       SetDrawBackgroundMask(REDRAW_FIELD);
3764     }
3765
3766     return FALSE;
3767   }
3768
3769   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3770
3771   // ---------- handle request buttons ----------
3772   result = RequestHandleEvents(req_state);
3773
3774   if (game_status != GAME_MODE_MAIN)
3775     StopAnimation();
3776
3777   UnmapToolButtons();
3778
3779   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3780
3781   RemapAllGadgets();
3782
3783   if (game_status == GAME_MODE_PLAYING)
3784   {
3785     SetPanelBackground();
3786     SetDrawBackgroundMask(REDRAW_DOOR_1);
3787   }
3788   else
3789   {
3790     SetDrawBackgroundMask(REDRAW_FIELD);
3791   }
3792
3793 #if defined(NETWORK_AVALIABLE)
3794   /* continue network game after request */
3795   if (options.network &&
3796       game_status == GAME_MODE_PLAYING &&
3797       req_state & REQUEST_WAIT_FOR_INPUT)
3798     SendToServer_ContinuePlaying();
3799 #endif
3800
3801   /* restore deactivated drawing when quick-loading level tape recording */
3802   if (tape.playing && tape.deactivate_display)
3803     TapeDeactivateDisplayOn();
3804
3805   return result;
3806 }
3807
3808 boolean Request(char *text, unsigned int req_state)
3809 {
3810   if (global.use_envelope_request)
3811     return RequestEnvelope(text, req_state);
3812   else
3813     return RequestDoor(text, req_state);
3814 }
3815
3816 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3817 {
3818   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3819   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3820   int compare_result;
3821
3822   if (dpo1->sort_priority != dpo2->sort_priority)
3823     compare_result = dpo1->sort_priority - dpo2->sort_priority;
3824   else
3825     compare_result = dpo1->nr - dpo2->nr;
3826
3827   return compare_result;
3828 }
3829
3830 void InitGraphicCompatibilityInfo_Doors()
3831 {
3832   struct
3833   {
3834     int door_token;
3835     int part_1, part_8;
3836     struct DoorInfo *door;
3837   }
3838   doors[] =
3839   {
3840     { DOOR_1,   IMG_DOOR_1_GFX_PART_1,  IMG_DOOR_1_GFX_PART_8,  &door_1 },
3841     { DOOR_2,   IMG_DOOR_2_GFX_PART_1,  IMG_DOOR_2_GFX_PART_8,  &door_2 },
3842
3843     { -1,       -1,                     -1,                     NULL    }
3844   };
3845   struct Rect door_rect_list[] =
3846   {
3847     { DX, DY, DXSIZE, DYSIZE },
3848     { VX, VY, VXSIZE, VYSIZE }
3849   };
3850   int i, j;
3851
3852   for (i = 0; doors[i].door_token != -1; i++)
3853   {
3854     int door_token = doors[i].door_token;
3855     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3856     int part_1 = doors[i].part_1;
3857     int part_8 = doors[i].part_8;
3858     int part_2 = part_1 + 1;
3859     int part_3 = part_1 + 2;
3860     struct DoorInfo *door = doors[i].door;
3861     struct Rect *door_rect = &door_rect_list[door_index];
3862     boolean door_gfx_redefined = FALSE;
3863
3864     /* check if any door part graphic definitions have been redefined */
3865
3866     for (j = 0; door_part_controls[j].door_token != -1; j++)
3867     {
3868       struct DoorPartControlInfo *dpc = &door_part_controls[j];
3869       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3870
3871       if (dpc->door_token == door_token && fi->redefined)
3872         door_gfx_redefined = TRUE;
3873     }
3874
3875     /* check for old-style door graphic/animation modifications */
3876
3877     if (!door_gfx_redefined)
3878     {
3879       if (door->anim_mode & ANIM_STATIC_PANEL)
3880       {
3881         door->panel.step_xoffset = 0;
3882         door->panel.step_yoffset = 0;
3883       }
3884
3885       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3886       {
3887         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3888         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3889         int num_door_steps, num_panel_steps;
3890
3891         /* remove door part graphics other than the two default wings */
3892
3893         for (j = 0; door_part_controls[j].door_token != -1; j++)
3894         {
3895           struct DoorPartControlInfo *dpc = &door_part_controls[j];
3896           struct GraphicInfo *g = &graphic_info[dpc->graphic];
3897
3898           if (dpc->graphic >= part_3 &&
3899               dpc->graphic <= part_8)
3900             g->bitmap = NULL;
3901         }
3902
3903         /* set graphics and screen positions of the default wings */
3904
3905         g_part_1->width  = door_rect->width;
3906         g_part_1->height = door_rect->height;
3907         g_part_2->width  = door_rect->width;
3908         g_part_2->height = door_rect->height;
3909         g_part_2->src_x = door_rect->width;
3910         g_part_2->src_y = g_part_1->src_y;
3911
3912         door->part_2.x = door->part_1.x;
3913         door->part_2.y = door->part_1.y;
3914
3915         if (door->width != -1)
3916         {
3917           g_part_1->width = door->width;
3918           g_part_2->width = door->width;
3919
3920           // special treatment for graphics and screen position of right wing
3921           g_part_2->src_x += door_rect->width - door->width;
3922           door->part_2.x  += door_rect->width - door->width;
3923         }
3924
3925         if (door->height != -1)
3926         {
3927           g_part_1->height = door->height;
3928           g_part_2->height = door->height;
3929
3930           // special treatment for graphics and screen position of bottom wing
3931           g_part_2->src_y += door_rect->height - door->height;
3932           door->part_2.y  += door_rect->height - door->height;
3933         }
3934
3935         /* set animation delays for the default wings and panels */
3936
3937         door->part_1.step_delay = door->step_delay;
3938         door->part_2.step_delay = door->step_delay;
3939         door->panel.step_delay  = door->step_delay;
3940
3941         /* set animation draw order for the default wings */
3942
3943         door->part_1.sort_priority = 2; /* draw left wing over ... */
3944         door->part_2.sort_priority = 1; /*          ... right wing */
3945
3946         /* set animation draw offset for the default wings */
3947
3948         if (door->anim_mode & ANIM_HORIZONTAL)
3949         {
3950           door->part_1.step_xoffset = door->step_offset;
3951           door->part_1.step_yoffset = 0;
3952           door->part_2.step_xoffset = door->step_offset * -1;
3953           door->part_2.step_yoffset = 0;
3954
3955           num_door_steps = g_part_1->width / door->step_offset;
3956         }
3957         else    // ANIM_VERTICAL
3958         {
3959           door->part_1.step_xoffset = 0;
3960           door->part_1.step_yoffset = door->step_offset;
3961           door->part_2.step_xoffset = 0;
3962           door->part_2.step_yoffset = door->step_offset * -1;
3963
3964           num_door_steps = g_part_1->height / door->step_offset;
3965         }
3966
3967         /* set animation draw offset for the default panels */
3968
3969         if (door->step_offset > 1)
3970         {
3971           num_panel_steps = 2 * door_rect->height / door->step_offset;
3972           door->panel.start_step = num_panel_steps - num_door_steps;
3973           door->panel.start_step_closing = door->panel.start_step;
3974         }
3975         else
3976         {
3977           num_panel_steps = door_rect->height / door->step_offset;
3978           door->panel.start_step = num_panel_steps - num_door_steps / 2;
3979           door->panel.start_step_closing = door->panel.start_step;
3980           door->panel.step_delay *= 2;
3981         }
3982       }
3983     }
3984   }
3985 }
3986
3987 void InitDoors()
3988 {
3989   int i;
3990
3991   for (i = 0; door_part_controls[i].door_token != -1; i++)
3992   {
3993     struct DoorPartControlInfo *dpc = &door_part_controls[i];
3994     struct DoorPartOrderInfo *dpo = &door_part_order[i];
3995
3996     /* initialize "start_step_opening" and "start_step_closing", if needed */
3997     if (dpc->pos->start_step_opening == 0 &&
3998         dpc->pos->start_step_closing == 0)
3999     {
4000       // dpc->pos->start_step_opening = dpc->pos->start_step;
4001       dpc->pos->start_step_closing = dpc->pos->start_step;
4002     }
4003
4004     /* fill structure for door part draw order (sorted below) */
4005     dpo->nr = i;
4006     dpo->sort_priority = dpc->pos->sort_priority;
4007   }
4008
4009   /* sort door part controls according to sort_priority and graphic number */
4010   qsort(door_part_order, MAX_DOOR_PARTS,
4011         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4012 }
4013
4014 unsigned int OpenDoor(unsigned int door_state)
4015 {
4016   if (door_state & DOOR_COPY_BACK)
4017   {
4018     if (door_state & DOOR_OPEN_1)
4019       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4020                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4021
4022     if (door_state & DOOR_OPEN_2)
4023       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4024                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4025
4026     door_state &= ~DOOR_COPY_BACK;
4027   }
4028
4029   return MoveDoor(door_state);
4030 }
4031
4032 unsigned int CloseDoor(unsigned int door_state)
4033 {
4034   unsigned int old_door_state = GetDoorState();
4035
4036   if (!(door_state & DOOR_NO_COPY_BACK))
4037   {
4038     if (old_door_state & DOOR_OPEN_1)
4039       BlitBitmap(backbuffer, bitmap_db_door_1,
4040                  DX, DY, DXSIZE, DYSIZE, 0, 0);
4041
4042     if (old_door_state & DOOR_OPEN_2)
4043       BlitBitmap(backbuffer, bitmap_db_door_2,
4044                  VX, VY, VXSIZE, VYSIZE, 0, 0);
4045
4046     door_state &= ~DOOR_NO_COPY_BACK;
4047   }
4048
4049   return MoveDoor(door_state);
4050 }
4051
4052 unsigned int GetDoorState()
4053 {
4054   return MoveDoor(DOOR_GET_STATE);
4055 }
4056
4057 unsigned int SetDoorState(unsigned int door_state)
4058 {
4059   return MoveDoor(door_state | DOOR_SET_STATE);
4060 }
4061
4062 int euclid(int a, int b)
4063 {
4064   return (b ? euclid(b, a % b) : a);
4065 }
4066
4067 unsigned int MoveDoor(unsigned int door_state)
4068 {
4069   struct Rect door_rect_list[] =
4070   {
4071     { DX, DY, DXSIZE, DYSIZE },
4072     { VX, VY, VXSIZE, VYSIZE }
4073   };
4074   static int door1 = DOOR_OPEN_1;
4075   static int door2 = DOOR_CLOSE_2;
4076   unsigned int door_delay = 0;
4077   unsigned int door_delay_value;
4078   int i;
4079
4080   if (door_state == DOOR_GET_STATE)
4081     return (door1 | door2);
4082
4083   if (door_state & DOOR_SET_STATE)
4084   {
4085     if (door_state & DOOR_ACTION_1)
4086       door1 = door_state & DOOR_ACTION_1;
4087     if (door_state & DOOR_ACTION_2)
4088       door2 = door_state & DOOR_ACTION_2;
4089
4090     return (door1 | door2);
4091   }
4092
4093   if (!(door_state & DOOR_FORCE_REDRAW))
4094   {
4095     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4096       door_state &= ~DOOR_OPEN_1;
4097     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4098       door_state &= ~DOOR_CLOSE_1;
4099     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4100       door_state &= ~DOOR_OPEN_2;
4101     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4102       door_state &= ~DOOR_CLOSE_2;
4103   }
4104
4105   if (global.autoplay_leveldir)
4106   {
4107     door_state |= DOOR_NO_DELAY;
4108     door_state &= ~DOOR_CLOSE_ALL;
4109   }
4110
4111   if (game_status == GAME_MODE_EDITOR)
4112     door_state |= DOOR_NO_DELAY;
4113
4114   if (door_state & DOOR_ACTION)
4115   {
4116     boolean door_panel_drawn[NUM_DOORS];
4117     boolean panel_has_doors[NUM_DOORS];
4118     boolean door_part_skip[MAX_DOOR_PARTS];
4119     boolean door_part_done[MAX_DOOR_PARTS];
4120     boolean door_part_done_all;
4121     int num_steps[MAX_DOOR_PARTS];
4122     int max_move_delay = 0;     // delay for complete animations of all doors
4123     int max_step_delay = 0;     // delay (ms) between two animation frames
4124     int num_move_steps = 0;     // number of animation steps for all doors
4125     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
4126     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
4127     int current_move_delay = 0;
4128     int start = 0;
4129     int k;
4130
4131     for (i = 0; i < NUM_DOORS; i++)
4132       panel_has_doors[i] = FALSE;
4133
4134     for (i = 0; i < MAX_DOOR_PARTS; i++)
4135     {
4136       struct DoorPartControlInfo *dpc = &door_part_controls[i];
4137       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4138       int door_token = dpc->door_token;
4139
4140       door_part_done[i] = FALSE;
4141       door_part_skip[i] = (!(door_state & door_token) ||
4142                            !g->bitmap);
4143     }
4144
4145     for (i = 0; i < MAX_DOOR_PARTS; i++)
4146     {
4147       int nr = door_part_order[i].nr;
4148       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4149       struct DoorPartPosInfo *pos = dpc->pos;
4150       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4151       int door_token = dpc->door_token;
4152       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4153       boolean is_panel = DOOR_PART_IS_PANEL(nr);
4154       int step_xoffset = ABS(pos->step_xoffset);
4155       int step_yoffset = ABS(pos->step_yoffset);
4156       int step_delay = pos->step_delay;
4157       int current_door_state = door_state & door_token;
4158       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4159       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4160       boolean part_opening = (is_panel ? door_closing : door_opening);
4161       int start_step = (part_opening ? pos->start_step_opening :
4162                         pos->start_step_closing);
4163       float move_xsize = (step_xoffset ? g->width  : 0);
4164       float move_ysize = (step_yoffset ? g->height : 0);
4165       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4166       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4167       int move_steps = (move_xsteps && move_ysteps ?
4168                         MIN(move_xsteps, move_ysteps) :
4169                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
4170       int move_delay = move_steps * step_delay;
4171
4172       if (door_part_skip[nr])
4173         continue;
4174
4175       max_move_delay = MAX(max_move_delay, move_delay);
4176       max_step_delay = (max_step_delay == 0 ? step_delay :
4177                         euclid(max_step_delay, step_delay));
4178       num_steps[nr] = move_steps;
4179
4180       if (!is_panel)
4181       {
4182         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4183
4184         panel_has_doors[door_index] = TRUE;
4185       }
4186     }
4187
4188     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
4189
4190     num_move_steps = max_move_delay / max_step_delay;
4191     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4192
4193     door_delay_value = max_step_delay;
4194
4195     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4196     {
4197       start = num_move_steps - 1;
4198     }
4199     else
4200     {
4201       /* opening door sound has priority over simultaneously closing door */
4202       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4203         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4204       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4205         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4206     }
4207
4208     for (k = start; k < num_move_steps; k++)
4209     {
4210       door_part_done_all = TRUE;
4211
4212       for (i = 0; i < NUM_DOORS; i++)
4213         door_panel_drawn[i] = FALSE;
4214
4215       for (i = 0; i < MAX_DOOR_PARTS; i++)
4216       {
4217         int nr = door_part_order[i].nr;
4218         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4219         struct DoorPartPosInfo *pos = dpc->pos;
4220         struct GraphicInfo *g = &graphic_info[dpc->graphic];
4221         int door_token = dpc->door_token;
4222         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4223         boolean is_panel = DOOR_PART_IS_PANEL(nr);
4224         boolean is_panel_and_door_has_closed = FALSE;
4225         struct Rect *door_rect = &door_rect_list[door_index];
4226         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4227                                   bitmap_db_door_2);
4228         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4229         int current_door_state = door_state & door_token;
4230         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4231         boolean door_closing = !door_opening;
4232         boolean part_opening = (is_panel ? door_closing : door_opening);
4233         boolean part_closing = !part_opening;
4234         int start_step = (part_opening ? pos->start_step_opening :
4235                           pos->start_step_closing);
4236         int step_delay = pos->step_delay;
4237         int step_factor = step_delay / max_step_delay;
4238         int k1 = (step_factor ? k / step_factor + 1 : k);
4239         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4240         int kk = MAX(0, k2);
4241         int g_src_x = 0;
4242         int g_src_y = 0;
4243         int src_x, src_y, src_xx, src_yy;
4244         int dst_x, dst_y, dst_xx, dst_yy;
4245         int width, height;
4246
4247         if (door_part_skip[nr])
4248           continue;
4249
4250         if (!(door_state & door_token))
4251           continue;
4252
4253         if (!g->bitmap)
4254           continue;
4255
4256         if (!is_panel)
4257         {
4258           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4259           int kk_door = MAX(0, k2_door);
4260           int sync_frame = kk_door * door_delay_value;
4261           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4262
4263           getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4264         }
4265
4266         // draw door panel
4267
4268         if (!door_panel_drawn[door_index])
4269         {
4270           ClearRectangle(drawto, door_rect->x, door_rect->y,
4271                          door_rect->width, door_rect->height);
4272
4273           door_panel_drawn[door_index] = TRUE;
4274         }
4275
4276         // draw opening or closing door parts
4277
4278         if (pos->step_xoffset < 0)      // door part on right side
4279         {
4280           src_xx = 0;
4281           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4282           width = g->width;
4283
4284           if (dst_xx + width > door_rect->width)
4285             width = door_rect->width - dst_xx;
4286         }
4287         else                            // door part on left side
4288         {
4289           src_xx = 0;
4290           dst_xx = pos->x - kk * pos->step_xoffset;
4291
4292           if (dst_xx < 0)
4293           {
4294             src_xx = ABS(dst_xx);
4295             dst_xx = 0;
4296           }
4297
4298           width = g->width - src_xx;
4299
4300           // printf("::: k == %d [%d] \n", k, start_step);
4301         }
4302
4303         if (pos->step_yoffset < 0)      // door part on bottom side
4304         {
4305           src_yy = 0;
4306           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4307           height = g->height;
4308
4309           if (dst_yy + height > door_rect->height)
4310             height = door_rect->height - dst_yy;
4311         }
4312         else                            // door part on top side
4313         {
4314           src_yy = 0;
4315           dst_yy = pos->y - kk * pos->step_yoffset;
4316
4317           if (dst_yy < 0)
4318           {
4319             src_yy = ABS(dst_yy);
4320             dst_yy = 0;
4321           }
4322
4323           height = g->height - src_yy;
4324         }
4325
4326         src_x = g_src_x + src_xx;
4327         src_y = g_src_y + src_yy;
4328
4329         dst_x = door_rect->x + dst_xx;
4330         dst_y = door_rect->y + dst_yy;
4331
4332         is_panel_and_door_has_closed =
4333           (is_panel &&
4334            door_closing &&
4335            panel_has_doors[door_index] &&
4336            k >= num_move_steps_doors_only - 1);
4337
4338         if (width  >= 0 && width  <= g->width &&
4339             height >= 0 && height <= g->height &&
4340             !is_panel_and_door_has_closed)
4341         {
4342           if (is_panel || !pos->draw_masked)
4343             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4344                        dst_x, dst_y);
4345           else
4346             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4347                              dst_x, dst_y);
4348         }
4349
4350         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4351
4352         if ((part_opening && (width < 0         || height < 0)) ||
4353             (part_closing && (width >= g->width && height >= g->height)))
4354           door_part_done[nr] = TRUE;
4355
4356         // continue door part animations, but not panel after door has closed
4357         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4358           door_part_done_all = FALSE;
4359       }
4360
4361       if (!(door_state & DOOR_NO_DELAY))
4362       {
4363         BackToFront();
4364
4365         if (game_status == GAME_MODE_MAIN)
4366           DoAnimation();
4367
4368         WaitUntilDelayReached(&door_delay, door_delay_value);
4369
4370         current_move_delay += max_step_delay;
4371       }
4372
4373       if (door_part_done_all)
4374         break;
4375     }
4376   }
4377
4378   if (door_state & DOOR_ACTION_1)
4379     door1 = door_state & DOOR_ACTION_1;
4380   if (door_state & DOOR_ACTION_2)
4381     door2 = door_state & DOOR_ACTION_2;
4382
4383   return (door1 | door2);
4384 }
4385
4386 void DrawSpecialEditorDoor()
4387 {
4388   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4389   int top_border_width = gfx1->width;
4390   int top_border_height = gfx1->height;
4391   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4392   int ex = EX - outer_border;
4393   int ey = EY - outer_border;
4394   int vy = VY - outer_border;
4395   int exsize = EXSIZE + 2 * outer_border;
4396
4397   /* draw bigger level editor toolbox window */
4398   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4399              top_border_width, top_border_height, ex, ey - top_border_height);
4400   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4401              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4402
4403   redraw_mask |= REDRAW_ALL;
4404 }
4405
4406 void UndrawSpecialEditorDoor()
4407 {
4408   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4409   int top_border_width = gfx1->width;
4410   int top_border_height = gfx1->height;
4411   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4412   int ex = EX - outer_border;
4413   int ey = EY - outer_border;
4414   int ey_top = ey - top_border_height;
4415   int exsize = EXSIZE + 2 * outer_border;
4416   int eysize = EYSIZE + 2 * outer_border;
4417
4418   /* draw normal tape recorder window */
4419   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4420   {
4421     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4422                ex, ey_top, top_border_width, top_border_height,
4423                ex, ey_top);
4424     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4425                ex, ey, exsize, eysize, ex, ey);
4426   }
4427   else
4428   {
4429     // if screen background is set to "[NONE]", clear editor toolbox window
4430     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4431     ClearRectangle(drawto, ex, ey, exsize, eysize);
4432   }
4433
4434   redraw_mask |= REDRAW_ALL;
4435 }
4436
4437
4438 /* ---------- new tool button stuff ---------------------------------------- */
4439
4440 static struct
4441 {
4442   int graphic;
4443   struct TextPosInfo *pos;
4444   int gadget_id;
4445   char *infotext;
4446 } toolbutton_info[NUM_TOOL_BUTTONS] =
4447 {
4448   {
4449     IMG_REQUEST_BUTTON_GFX_YES,         &request.button.yes,
4450     TOOL_CTRL_ID_YES,                   "yes"
4451   },
4452   {
4453     IMG_REQUEST_BUTTON_GFX_NO,          &request.button.no,
4454     TOOL_CTRL_ID_NO,                    "no"
4455   },
4456   {
4457     IMG_REQUEST_BUTTON_GFX_CONFIRM,     &request.button.confirm,
4458     TOOL_CTRL_ID_CONFIRM,               "confirm"
4459   },
4460   {
4461     IMG_REQUEST_BUTTON_GFX_PLAYER_1,    &request.button.player_1,
4462     TOOL_CTRL_ID_PLAYER_1,              "player 1"
4463   },
4464   {
4465     IMG_REQUEST_BUTTON_GFX_PLAYER_2,    &request.button.player_2,
4466     TOOL_CTRL_ID_PLAYER_2,              "player 2"
4467   },
4468   {
4469     IMG_REQUEST_BUTTON_GFX_PLAYER_3,    &request.button.player_3,
4470     TOOL_CTRL_ID_PLAYER_3,              "player 3"
4471   },
4472   {
4473     IMG_REQUEST_BUTTON_GFX_PLAYER_4,    &request.button.player_4,
4474     TOOL_CTRL_ID_PLAYER_4,              "player 4"
4475   }
4476 };
4477
4478 void CreateToolButtons()
4479 {
4480   int i;
4481
4482   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4483   {
4484     struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4485     struct TextPosInfo *pos = toolbutton_info[i].pos;
4486     struct GadgetInfo *gi;
4487     Bitmap *deco_bitmap = None;
4488     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4489     unsigned int event_mask = GD_EVENT_RELEASED;
4490     int dx = DX;
4491     int dy = DY;
4492     int gd_x = gfx->src_x;
4493     int gd_y = gfx->src_y;
4494     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4495     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4496     int id = i;
4497
4498     if (global.use_envelope_request)
4499       setRequestPosition(&dx, &dy, TRUE);
4500
4501     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4502     {
4503       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4504
4505       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4506                             pos->size, &deco_bitmap, &deco_x, &deco_y);
4507       deco_xpos = (gfx->width  - pos->size) / 2;
4508       deco_ypos = (gfx->height - pos->size) / 2;
4509     }
4510
4511     gi = CreateGadget(GDI_CUSTOM_ID, id,
4512                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
4513                       GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4514                       GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4515                       GDI_WIDTH, gfx->width,
4516                       GDI_HEIGHT, gfx->height,
4517                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4518                       GDI_STATE, GD_BUTTON_UNPRESSED,
4519                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4520                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4521                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4522                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4523                       GDI_DECORATION_SIZE, pos->size, pos->size,
4524                       GDI_DECORATION_SHIFTING, 1, 1,
4525                       GDI_DIRECT_DRAW, FALSE,
4526                       GDI_EVENT_MASK, event_mask,
4527                       GDI_CALLBACK_ACTION, HandleToolButtons,
4528                       GDI_END);
4529
4530     if (gi == NULL)
4531       Error(ERR_EXIT, "cannot create gadget");
4532
4533     tool_gadget[id] = gi;
4534   }
4535 }
4536
4537 void FreeToolButtons()
4538 {
4539   int i;
4540
4541   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4542     FreeGadget(tool_gadget[i]);
4543 }
4544
4545 static void UnmapToolButtons()
4546 {
4547   int i;
4548
4549   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4550     UnmapGadget(tool_gadget[i]);
4551 }
4552
4553 static void HandleToolButtons(struct GadgetInfo *gi)
4554 {
4555   request_gadget_id = gi->custom_id;
4556 }
4557
4558 static struct Mapping_EM_to_RND_object
4559 {
4560   int element_em;
4561   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
4562   boolean is_backside;                  /* backside of moving element */
4563
4564   int element_rnd;
4565   int action;
4566   int direction;
4567 }
4568 em_object_mapping_list[] =
4569 {
4570   {
4571     Xblank,                             TRUE,   FALSE,
4572     EL_EMPTY,                           -1, -1
4573   },
4574   {
4575     Yacid_splash_eB,                    FALSE,  FALSE,
4576     EL_ACID_SPLASH_RIGHT,               -1, -1
4577   },
4578   {
4579     Yacid_splash_wB,                    FALSE,  FALSE,
4580     EL_ACID_SPLASH_LEFT,                -1, -1
4581   },
4582
4583 #ifdef EM_ENGINE_BAD_ROLL
4584   {
4585     Xstone_force_e,                     FALSE,  FALSE,
4586     EL_ROCK,                            -1, MV_BIT_RIGHT
4587   },
4588   {
4589     Xstone_force_w,                     FALSE,  FALSE,
4590     EL_ROCK,                            -1, MV_BIT_LEFT
4591   },
4592   {
4593     Xnut_force_e,                       FALSE,  FALSE,
4594     EL_NUT,                             -1, MV_BIT_RIGHT
4595   },
4596   {
4597     Xnut_force_w,                       FALSE,  FALSE,
4598     EL_NUT,                             -1, MV_BIT_LEFT
4599   },
4600   {
4601     Xspring_force_e,                    FALSE,  FALSE,
4602     EL_SPRING,                          -1, MV_BIT_RIGHT
4603   },
4604   {
4605     Xspring_force_w,                    FALSE,  FALSE,
4606     EL_SPRING,                          -1, MV_BIT_LEFT
4607   },
4608   {
4609     Xemerald_force_e,                   FALSE,  FALSE,
4610     EL_EMERALD,                         -1, MV_BIT_RIGHT
4611   },
4612   {
4613     Xemerald_force_w,                   FALSE,  FALSE,
4614     EL_EMERALD,                         -1, MV_BIT_LEFT
4615   },
4616   {
4617     Xdiamond_force_e,                   FALSE,  FALSE,
4618     EL_DIAMOND,                         -1, MV_BIT_RIGHT
4619   },
4620   {
4621     Xdiamond_force_w,                   FALSE,  FALSE,
4622     EL_DIAMOND,                         -1, MV_BIT_LEFT
4623   },
4624   {
4625     Xbomb_force_e,                      FALSE,  FALSE,
4626     EL_BOMB,                            -1, MV_BIT_RIGHT
4627   },
4628   {
4629     Xbomb_force_w,                      FALSE,  FALSE,
4630     EL_BOMB,                            -1, MV_BIT_LEFT
4631   },
4632 #endif  /* EM_ENGINE_BAD_ROLL */
4633
4634   {
4635     Xstone,                             TRUE,   FALSE,
4636     EL_ROCK,                            -1, -1
4637   },
4638   {
4639     Xstone_pause,                       FALSE,  FALSE,
4640     EL_ROCK,                            -1, -1
4641   },
4642   {
4643     Xstone_fall,                        FALSE,  FALSE,
4644     EL_ROCK,                            -1, -1
4645   },
4646   {
4647     Ystone_s,                           FALSE,  FALSE,
4648     EL_ROCK,                            ACTION_FALLING, -1
4649   },
4650   {
4651     Ystone_sB,                          FALSE,  TRUE,
4652     EL_ROCK,                            ACTION_FALLING, -1
4653   },
4654   {
4655     Ystone_e,                           FALSE,  FALSE,
4656     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
4657   },
4658   {
4659     Ystone_eB,                          FALSE,  TRUE,
4660     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
4661   },
4662   {
4663     Ystone_w,                           FALSE,  FALSE,
4664     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
4665   },
4666   {
4667     Ystone_wB,                          FALSE,  TRUE,
4668     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
4669   },
4670   {
4671     Xnut,                               TRUE,   FALSE,
4672     EL_NUT,                             -1, -1
4673   },
4674   {
4675     Xnut_pause,                         FALSE,  FALSE,
4676     EL_NUT,                             -1, -1
4677   },
4678   {
4679     Xnut_fall,                          FALSE,  FALSE,
4680     EL_NUT,                             -1, -1
4681   },
4682   {
4683     Ynut_s,                             FALSE,  FALSE,
4684     EL_NUT,                             ACTION_FALLING, -1
4685   },
4686   {
4687     Ynut_sB,                            FALSE,  TRUE,
4688     EL_NUT,                             ACTION_FALLING, -1
4689   },
4690   {
4691     Ynut_e,                             FALSE,  FALSE,
4692     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
4693   },
4694   {
4695     Ynut_eB,                            FALSE,  TRUE,
4696     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
4697   },
4698   {
4699     Ynut_w,                             FALSE,  FALSE,
4700     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
4701   },
4702   {
4703     Ynut_wB,                            FALSE,  TRUE,
4704     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
4705   },
4706   {
4707     Xbug_n,                             TRUE,   FALSE,
4708     EL_BUG_UP,                          -1, -1
4709   },
4710   {
4711     Xbug_e,                             TRUE,   FALSE,
4712     EL_BUG_RIGHT,                       -1, -1
4713   },
4714   {
4715     Xbug_s,                             TRUE,   FALSE,
4716     EL_BUG_DOWN,                        -1, -1
4717   },
4718   {
4719     Xbug_w,                             TRUE,   FALSE,
4720     EL_BUG_LEFT,                        -1, -1
4721   },
4722   {
4723     Xbug_gon,                           FALSE,  FALSE,
4724     EL_BUG_UP,                          -1, -1
4725   },
4726   {
4727     Xbug_goe,                           FALSE,  FALSE,
4728     EL_BUG_RIGHT,                       -1, -1
4729   },
4730   {
4731     Xbug_gos,                           FALSE,  FALSE,
4732     EL_BUG_DOWN,                        -1, -1
4733   },
4734   {
4735     Xbug_gow,                           FALSE,  FALSE,
4736     EL_BUG_LEFT,                        -1, -1
4737   },
4738   {
4739     Ybug_n,                             FALSE,  FALSE,
4740     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
4741   },
4742   {
4743     Ybug_nB,                            FALSE,  TRUE,
4744     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
4745   },
4746   {
4747     Ybug_e,                             FALSE,  FALSE,
4748     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
4749   },
4750   {
4751     Ybug_eB,                            FALSE,  TRUE,
4752     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
4753   },
4754   {
4755     Ybug_s,                             FALSE,  FALSE,
4756     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
4757   },
4758   {
4759     Ybug_sB,                            FALSE,  TRUE,
4760     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
4761   },
4762   {
4763     Ybug_w,                             FALSE,  FALSE,
4764     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
4765   },
4766   {
4767     Ybug_wB,                            FALSE,  TRUE,
4768     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
4769   },
4770   {
4771     Ybug_w_n,                           FALSE,  FALSE,
4772     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4773   },
4774   {
4775     Ybug_n_e,                           FALSE,  FALSE,
4776     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4777   },
4778   {
4779     Ybug_e_s,                           FALSE,  FALSE,
4780     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4781   },
4782   {
4783     Ybug_s_w,                           FALSE,  FALSE,
4784     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4785   },
4786   {
4787     Ybug_e_n,                           FALSE,  FALSE,
4788     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4789   },
4790   {
4791     Ybug_s_e,                           FALSE,  FALSE,
4792     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4793   },
4794   {
4795     Ybug_w_s,                           FALSE,  FALSE,
4796     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4797   },
4798   {
4799     Ybug_n_w,                           FALSE,  FALSE,
4800     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4801   },
4802   {
4803     Ybug_stone,                         FALSE,  FALSE,
4804     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
4805   },
4806   {
4807     Ybug_spring,                        FALSE,  FALSE,
4808     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
4809   },
4810   {
4811     Xtank_n,                            TRUE,   FALSE,
4812     EL_SPACESHIP_UP,                    -1, -1
4813   },
4814   {
4815     Xtank_e,                            TRUE,   FALSE,
4816     EL_SPACESHIP_RIGHT,                 -1, -1
4817   },
4818   {
4819     Xtank_s,                            TRUE,   FALSE,
4820     EL_SPACESHIP_DOWN,                  -1, -1
4821   },
4822   {
4823     Xtank_w,                            TRUE,   FALSE,
4824     EL_SPACESHIP_LEFT,                  -1, -1
4825   },
4826   {
4827     Xtank_gon,                          FALSE,  FALSE,
4828     EL_SPACESHIP_UP,                    -1, -1
4829   },
4830   {
4831     Xtank_goe,                          FALSE,  FALSE,
4832     EL_SPACESHIP_RIGHT,                 -1, -1
4833   },
4834   {
4835     Xtank_gos,                          FALSE,  FALSE,
4836     EL_SPACESHIP_DOWN,                  -1, -1
4837   },
4838   {
4839     Xtank_gow,                          FALSE,  FALSE,
4840     EL_SPACESHIP_LEFT,                  -1, -1
4841   },
4842   {
4843     Ytank_n,                            FALSE,  FALSE,
4844     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
4845   },
4846   {
4847     Ytank_nB,                           FALSE,  TRUE,
4848     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
4849   },
4850   {
4851     Ytank_e,                            FALSE,  FALSE,
4852     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
4853   },
4854   {
4855     Ytank_eB,                           FALSE,  TRUE,
4856     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
4857   },
4858   {
4859     Ytank_s,                            FALSE,  FALSE,
4860     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
4861   },
4862   {
4863     Ytank_sB,                           FALSE,  TRUE,
4864     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
4865   },
4866   {
4867     Ytank_w,                            FALSE,  FALSE,
4868     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
4869   },
4870   {
4871     Ytank_wB,                           FALSE,  TRUE,
4872     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
4873   },
4874   {
4875     Ytank_w_n,                          FALSE,  FALSE,
4876     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4877   },
4878   {
4879     Ytank_n_e,                          FALSE,  FALSE,
4880     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4881   },
4882   {
4883     Ytank_e_s,                          FALSE,  FALSE,
4884     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4885   },
4886   {
4887     Ytank_s_w,                          FALSE,  FALSE,
4888     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4889   },
4890   {
4891     Ytank_e_n,                          FALSE,  FALSE,
4892     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4893   },
4894   {
4895     Ytank_s_e,                          FALSE,  FALSE,
4896     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4897   },
4898   {
4899     Ytank_w_s,                          FALSE,  FALSE,
4900     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4901   },
4902   {
4903     Ytank_n_w,                          FALSE,  FALSE,
4904     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4905   },
4906   {
4907     Ytank_stone,                        FALSE,  FALSE,
4908     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
4909   },
4910   {
4911     Ytank_spring,                       FALSE,  FALSE,
4912     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
4913   },
4914   {
4915     Xandroid,                           TRUE,   FALSE,
4916     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
4917   },
4918   {
4919     Xandroid_1_n,                       FALSE,  FALSE,
4920     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
4921   },
4922   {
4923     Xandroid_2_n,                       FALSE,  FALSE,
4924     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
4925   },
4926   {
4927     Xandroid_1_e,                       FALSE,  FALSE,
4928     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
4929   },
4930   {
4931     Xandroid_2_e,                       FALSE,  FALSE,
4932     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
4933   },
4934   {
4935     Xandroid_1_w,                       FALSE,  FALSE,
4936     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
4937   },
4938   {
4939     Xandroid_2_w,                       FALSE,  FALSE,
4940     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
4941   },
4942   {
4943     Xandroid_1_s,                       FALSE,  FALSE,
4944     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
4945   },
4946   {
4947     Xandroid_2_s,                       FALSE,  FALSE,
4948     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
4949   },
4950   {
4951     Yandroid_n,                         FALSE,  FALSE,
4952     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
4953   },
4954   {
4955     Yandroid_nB,                        FALSE,  TRUE,
4956     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
4957   },
4958   {
4959     Yandroid_ne,                        FALSE,  FALSE,
4960     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
4961   },
4962   {
4963     Yandroid_neB,                       FALSE,  TRUE,
4964     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
4965   },
4966   {
4967     Yandroid_e,                         FALSE,  FALSE,
4968     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
4969   },
4970   {
4971     Yandroid_eB,                        FALSE,  TRUE,
4972     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
4973   },
4974   {
4975     Yandroid_se,                        FALSE,  FALSE,
4976     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
4977   },
4978   {
4979     Yandroid_seB,                       FALSE,  TRUE,
4980     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4981   },
4982   {
4983     Yandroid_s,                         FALSE,  FALSE,
4984     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
4985   },
4986   {
4987     Yandroid_sB,                        FALSE,  TRUE,
4988     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
4989   },
4990   {
4991     Yandroid_sw,                        FALSE,  FALSE,
4992     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
4993   },
4994   {
4995     Yandroid_swB,                       FALSE,  TRUE,
4996     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
4997   },
4998   {
4999     Yandroid_w,                         FALSE,  FALSE,
5000     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5001   },
5002   {
5003     Yandroid_wB,                        FALSE,  TRUE,
5004     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5005   },
5006   {
5007     Yandroid_nw,                        FALSE,  FALSE,
5008     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
5009   },
5010   {
5011     Yandroid_nwB,                       FALSE,  TRUE,
5012     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
5013   },
5014   {
5015     Xspring,                            TRUE,   FALSE,
5016     EL_SPRING,                          -1, -1
5017   },
5018   {
5019     Xspring_pause,                      FALSE,  FALSE,
5020     EL_SPRING,                          -1, -1
5021   },
5022   {
5023     Xspring_e,                          FALSE,  FALSE,
5024     EL_SPRING,                          -1, -1
5025   },
5026   {
5027     Xspring_w,                          FALSE,  FALSE,
5028     EL_SPRING,                          -1, -1
5029   },
5030   {
5031     Xspring_fall,                       FALSE,  FALSE,
5032     EL_SPRING,                          -1, -1
5033   },
5034   {
5035     Yspring_s,                          FALSE,  FALSE,
5036     EL_SPRING,                          ACTION_FALLING, -1
5037   },
5038   {
5039     Yspring_sB,                         FALSE,  TRUE,
5040     EL_SPRING,                          ACTION_FALLING, -1
5041   },
5042   {
5043     Yspring_e,                          FALSE,  FALSE,
5044     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5045   },
5046   {
5047     Yspring_eB,                         FALSE,  TRUE,
5048     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5049   },
5050   {
5051     Yspring_w,                          FALSE,  FALSE,
5052     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5053   },
5054   {
5055     Yspring_wB,                         FALSE,  TRUE,
5056     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5057   },
5058   {
5059     Yspring_kill_e,                     FALSE,  FALSE,
5060     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5061   },
5062   {
5063     Yspring_kill_eB,                    FALSE,  TRUE,
5064     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5065   },
5066   {
5067     Yspring_kill_w,                     FALSE,  FALSE,
5068     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5069   },
5070   {
5071     Yspring_kill_wB,                    FALSE,  TRUE,
5072     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5073   },
5074   {
5075     Xeater_n,                           TRUE,   FALSE,
5076     EL_YAMYAM_UP,                       -1, -1
5077   },
5078   {
5079     Xeater_e,                           TRUE,   FALSE,
5080     EL_YAMYAM_RIGHT,                    -1, -1
5081   },
5082   {
5083     Xeater_w,                           TRUE,   FALSE,
5084     EL_YAMYAM_LEFT,                     -1, -1
5085   },
5086   {
5087     Xeater_s,                           TRUE,   FALSE,
5088     EL_YAMYAM_DOWN,                     -1, -1
5089   },
5090   {
5091     Yeater_n,                           FALSE,  FALSE,
5092     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5093   },
5094   {
5095     Yeater_nB,                          FALSE,  TRUE,
5096     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5097   },
5098   {
5099     Yeater_e,                           FALSE,  FALSE,
5100     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5101   },
5102   {
5103     Yeater_eB,                          FALSE,  TRUE,
5104     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5105   },
5106   {
5107     Yeater_s,                           FALSE,  FALSE,
5108     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5109   },
5110   {
5111     Yeater_sB,                          FALSE,  TRUE,
5112     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5113   },
5114   {
5115     Yeater_w,                           FALSE,  FALSE,
5116     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5117   },
5118   {
5119     Yeater_wB,                          FALSE,  TRUE,
5120     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5121   },
5122   {
5123     Yeater_stone,                       FALSE,  FALSE,
5124     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
5125   },
5126   {
5127     Yeater_spring,                      FALSE,  FALSE,
5128     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
5129   },
5130   {
5131     Xalien,                             TRUE,   FALSE,
5132     EL_ROBOT,                           -1, -1
5133   },
5134   {
5135     Xalien_pause,                       FALSE,  FALSE,
5136     EL_ROBOT,                           -1, -1
5137   },
5138   {
5139     Yalien_n,                           FALSE,  FALSE,
5140     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5141   },
5142   {
5143     Yalien_nB,                          FALSE,  TRUE,
5144     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5145   },
5146   {
5147     Yalien_e,                           FALSE,  FALSE,
5148     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5149   },
5150   {
5151     Yalien_eB,                          FALSE,  TRUE,
5152     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5153   },
5154   {
5155     Yalien_s,                           FALSE,  FALSE,
5156     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5157   },
5158   {
5159     Yalien_sB,                          FALSE,  TRUE,
5160     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5161   },
5162   {
5163     Yalien_w,                           FALSE,  FALSE,
5164     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5165   },
5166   {
5167     Yalien_wB,                          FALSE,  TRUE,
5168     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5169   },
5170   {
5171     Yalien_stone,                       FALSE,  FALSE,
5172     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
5173   },
5174   {
5175     Yalien_spring,                      FALSE,  FALSE,
5176     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
5177   },
5178   {
5179     Xemerald,                           TRUE,   FALSE,
5180     EL_EMERALD,                         -1, -1
5181   },
5182   {
5183     Xemerald_pause,                     FALSE,  FALSE,
5184     EL_EMERALD,                         -1, -1
5185   },
5186   {
5187     Xemerald_fall,                      FALSE,  FALSE,
5188     EL_EMERALD,                         -1, -1
5189   },
5190   {
5191     Xemerald_shine,                     FALSE,  FALSE,
5192     EL_EMERALD,                         ACTION_TWINKLING, -1
5193   },
5194   {
5195     Yemerald_s,                         FALSE,  FALSE,
5196     EL_EMERALD,                         ACTION_FALLING, -1
5197   },
5198   {
5199     Yemerald_sB,                        FALSE,  TRUE,
5200     EL_EMERALD,                         ACTION_FALLING, -1
5201   },
5202   {
5203     Yemerald_e,                         FALSE,  FALSE,
5204     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5205   },
5206   {
5207     Yemerald_eB,                        FALSE,  TRUE,
5208     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5209   },
5210   {
5211     Yemerald_w,                         FALSE,  FALSE,
5212     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5213   },
5214   {
5215     Yemerald_wB,                        FALSE,  TRUE,
5216     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5217   },
5218   {
5219     Yemerald_eat,                       FALSE,  FALSE,
5220     EL_EMERALD,                         ACTION_COLLECTING, -1
5221   },
5222   {
5223     Yemerald_stone,                     FALSE,  FALSE,
5224     EL_NUT,                             ACTION_BREAKING, -1
5225   },
5226   {
5227     Xdiamond,                           TRUE,   FALSE,
5228     EL_DIAMOND,                         -1, -1
5229   },
5230   {
5231     Xdiamond_pause,                     FALSE,  FALSE,
5232     EL_DIAMOND,                         -1, -1
5233   },
5234   {
5235     Xdiamond_fall,                      FALSE,  FALSE,
5236     EL_DIAMOND,                         -1, -1
5237   },
5238   {
5239     Xdiamond_shine,                     FALSE,  FALSE,
5240     EL_DIAMOND,                         ACTION_TWINKLING, -1
5241   },
5242   {
5243     Ydiamond_s,                         FALSE,  FALSE,
5244     EL_DIAMOND,                         ACTION_FALLING, -1
5245   },
5246   {
5247     Ydiamond_sB,                        FALSE,  TRUE,
5248     EL_DIAMOND,                         ACTION_FALLING, -1
5249   },
5250   {
5251     Ydiamond_e,                         FALSE,  FALSE,
5252     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5253   },
5254   {
5255     Ydiamond_eB,                        FALSE,  TRUE,
5256     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5257   },
5258   {
5259     Ydiamond_w,                         FALSE,  FALSE,
5260     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5261   },
5262   {
5263     Ydiamond_wB,                        FALSE,  TRUE,
5264     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5265   },
5266   {
5267     Ydiamond_eat,                       FALSE,  FALSE,
5268     EL_DIAMOND,                         ACTION_COLLECTING, -1
5269   },
5270   {
5271     Ydiamond_stone,                     FALSE,  FALSE,
5272     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
5273   },
5274   {
5275     Xdrip_fall,                         TRUE,   FALSE,
5276     EL_AMOEBA_DROP,                     -1, -1
5277   },
5278   {
5279     Xdrip_stretch,                      FALSE,  FALSE,
5280     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5281   },
5282   {
5283     Xdrip_stretchB,                     FALSE,  TRUE,
5284     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5285   },
5286   {
5287     Xdrip_eat,                          FALSE,  FALSE,
5288     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
5289   },
5290   {
5291     Ydrip_s1,                           FALSE,  FALSE,
5292     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5293   },
5294   {
5295     Ydrip_s1B,                          FALSE,  TRUE,
5296     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5297   },
5298   {
5299     Ydrip_s2,                           FALSE,  FALSE,
5300     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5301   },
5302   {
5303     Ydrip_s2B,                          FALSE,  TRUE,
5304     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5305   },
5306   {
5307     Xbomb,                              TRUE,   FALSE,
5308     EL_BOMB,                            -1, -1
5309   },
5310   {
5311     Xbomb_pause,                        FALSE,  FALSE,
5312     EL_BOMB,                            -1, -1
5313   },
5314   {
5315     Xbomb_fall,                         FALSE,  FALSE,
5316     EL_BOMB,                            -1, -1
5317   },
5318   {
5319     Ybomb_s,                            FALSE,  FALSE,
5320     EL_BOMB,                            ACTION_FALLING, -1
5321   },
5322   {
5323     Ybomb_sB,                           FALSE,  TRUE,
5324     EL_BOMB,                            ACTION_FALLING, -1
5325   },
5326   {
5327     Ybomb_e,                            FALSE,  FALSE,
5328     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5329   },
5330   {
5331     Ybomb_eB,                           FALSE,  TRUE,
5332     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5333   },
5334   {
5335     Ybomb_w,                            FALSE,  FALSE,
5336     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5337   },
5338   {
5339     Ybomb_wB,                           FALSE,  TRUE,
5340     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5341   },
5342   {
5343     Ybomb_eat,                          FALSE,  FALSE,
5344     EL_BOMB,                            ACTION_ACTIVATING, -1
5345   },
5346   {
5347     Xballoon,                           TRUE,   FALSE,
5348     EL_BALLOON,                         -1, -1
5349   },
5350   {
5351     Yballoon_n,                         FALSE,  FALSE,
5352     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5353   },
5354   {
5355     Yballoon_nB,                        FALSE,  TRUE,
5356     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5357   },
5358   {
5359     Yballoon_e,                         FALSE,  FALSE,
5360     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5361   },
5362   {
5363     Yballoon_eB,                        FALSE,  TRUE,
5364     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5365   },
5366   {
5367     Yballoon_s,                         FALSE,  FALSE,
5368     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5369   },
5370   {
5371     Yballoon_sB,                        FALSE,  TRUE,
5372     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5373   },
5374   {
5375     Yballoon_w,                         FALSE,  FALSE,
5376     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5377   },
5378   {
5379     Yballoon_wB,                        FALSE,  TRUE,
5380     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5381   },
5382   {
5383     Xgrass,                             TRUE,   FALSE,
5384     EL_EMC_GRASS,                       -1, -1
5385   },
5386   {
5387     Ygrass_nB,                          FALSE,  FALSE,
5388     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
5389   },
5390   {
5391     Ygrass_eB,                          FALSE,  FALSE,
5392     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
5393   },
5394   {
5395     Ygrass_sB,                          FALSE,  FALSE,
5396     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
5397   },
5398   {
5399     Ygrass_wB,                          FALSE,  FALSE,
5400     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
5401   },
5402   {
5403     Xdirt,                              TRUE,   FALSE,
5404     EL_SAND,                            -1, -1
5405   },
5406   {
5407     Ydirt_nB,                           FALSE,  FALSE,
5408     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
5409   },
5410   {
5411     Ydirt_eB,                           FALSE,  FALSE,
5412     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
5413   },
5414   {
5415     Ydirt_sB,                           FALSE,  FALSE,
5416     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
5417   },
5418   {
5419     Ydirt_wB,                           FALSE,  FALSE,
5420     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
5421   },
5422   {
5423     Xacid_ne,                           TRUE,   FALSE,
5424     EL_ACID_POOL_TOPRIGHT,              -1, -1
5425   },
5426   {
5427     Xacid_se,                           TRUE,   FALSE,
5428     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
5429   },
5430   {
5431     Xacid_s,                            TRUE,   FALSE,
5432     EL_ACID_POOL_BOTTOM,                -1, -1
5433   },
5434   {
5435     Xacid_sw,                           TRUE,   FALSE,
5436     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
5437   },
5438   {
5439     Xacid_nw,                           TRUE,   FALSE,
5440     EL_ACID_POOL_TOPLEFT,               -1, -1
5441   },
5442   {
5443     Xacid_1,                            TRUE,   FALSE,
5444     EL_ACID,                            -1, -1
5445   },
5446   {
5447     Xacid_2,                            FALSE,  FALSE,
5448     EL_ACID,                            -1, -1
5449   },
5450   {
5451     Xacid_3,                            FALSE,  FALSE,
5452     EL_ACID,                            -1, -1
5453   },
5454   {
5455     Xacid_4,                            FALSE,  FALSE,
5456     EL_ACID,                            -1, -1
5457   },
5458   {
5459     Xacid_5,                            FALSE,  FALSE,
5460     EL_ACID,                            -1, -1
5461   },
5462   {
5463     Xacid_6,                            FALSE,  FALSE,
5464     EL_ACID,                            -1, -1
5465   },
5466   {
5467     Xacid_7,                            FALSE,  FALSE,
5468     EL_ACID,                            -1, -1
5469   },
5470   {
5471     Xacid_8,                            FALSE,  FALSE,
5472     EL_ACID,                            -1, -1
5473   },
5474   {
5475     Xball_1,                            TRUE,   FALSE,
5476     EL_EMC_MAGIC_BALL,                  -1, -1
5477   },
5478   {
5479     Xball_1B,                           FALSE,  FALSE,
5480     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5481   },
5482   {
5483     Xball_2,                            FALSE,  FALSE,
5484     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5485   },
5486   {
5487     Xball_2B,                           FALSE,  FALSE,
5488     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5489   },
5490   {
5491     Yball_eat,                          FALSE,  FALSE,
5492     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
5493   },
5494   {
5495     Ykey_1_eat,                         FALSE,  FALSE,
5496     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
5497   },
5498   {
5499     Ykey_2_eat,                         FALSE,  FALSE,
5500     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
5501   },
5502   {
5503     Ykey_3_eat,                         FALSE,  FALSE,
5504     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
5505   },
5506   {
5507     Ykey_4_eat,                         FALSE,  FALSE,
5508     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
5509   },
5510   {
5511     Ykey_5_eat,                         FALSE,  FALSE,
5512     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
5513   },
5514   {
5515     Ykey_6_eat,                         FALSE,  FALSE,
5516     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
5517   },
5518   {
5519     Ykey_7_eat,                         FALSE,  FALSE,
5520     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
5521   },
5522   {
5523     Ykey_8_eat,                         FALSE,  FALSE,
5524     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
5525   },
5526   {
5527     Ylenses_eat,                        FALSE,  FALSE,
5528     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
5529   },
5530   {
5531     Ymagnify_eat,                       FALSE,  FALSE,
5532     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
5533   },
5534   {
5535     Ygrass_eat,                         FALSE,  FALSE,
5536     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
5537   },
5538   {
5539     Ydirt_eat,                          FALSE,  FALSE,
5540     EL_SAND,                            ACTION_SNAPPING, -1
5541   },
5542   {
5543     Xgrow_ns,                           TRUE,   FALSE,
5544     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
5545   },
5546   {
5547     Ygrow_ns_eat,                       FALSE,  FALSE,
5548     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
5549   },
5550   {
5551     Xgrow_ew,                           TRUE,   FALSE,
5552     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
5553   },
5554   {
5555     Ygrow_ew_eat,                       FALSE,  FALSE,
5556     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
5557   },
5558   {
5559     Xwonderwall,                        TRUE,   FALSE,
5560     EL_MAGIC_WALL,                      -1, -1
5561   },
5562   {
5563     XwonderwallB,                       FALSE,  FALSE,
5564     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
5565   },
5566   {
5567     Xamoeba_1,                          TRUE,   FALSE,
5568     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5569   },
5570   {
5571     Xamoeba_2,                          FALSE,  FALSE,
5572     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5573   },
5574   {
5575     Xamoeba_3,                          FALSE,  FALSE,
5576     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5577   },
5578   {
5579     Xamoeba_4,                          FALSE,  FALSE,
5580     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
5581   },
5582   {
5583     Xamoeba_5,                          TRUE,   FALSE,
5584     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5585   },
5586   {
5587     Xamoeba_6,                          FALSE,  FALSE,
5588     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5589   },
5590   {
5591     Xamoeba_7,                          FALSE,  FALSE,
5592     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5593   },
5594   {
5595     Xamoeba_8,                          FALSE,  FALSE,
5596     EL_AMOEBA_WET,                      ACTION_OTHER, -1
5597   },
5598   {
5599     Xdoor_1,                            TRUE,   FALSE,
5600     EL_EM_GATE_1,                       -1, -1
5601   },
5602   {
5603     Xdoor_2,                            TRUE,   FALSE,
5604     EL_EM_GATE_2,                       -1, -1
5605   },
5606   {
5607     Xdoor_3,                            TRUE,   FALSE,
5608     EL_EM_GATE_3,                       -1, -1
5609   },
5610   {
5611     Xdoor_4,                            TRUE,   FALSE,
5612     EL_EM_GATE_4,                       -1, -1
5613   },
5614   {
5615     Xdoor_5,                            TRUE,   FALSE,
5616     EL_EMC_GATE_5,                      -1, -1
5617   },
5618   {
5619     Xdoor_6,                            TRUE,   FALSE,
5620     EL_EMC_GATE_6,                      -1, -1
5621   },
5622   {
5623     Xdoor_7,                            TRUE,   FALSE,
5624     EL_EMC_GATE_7,                      -1, -1
5625   },
5626   {
5627     Xdoor_8,                            TRUE,   FALSE,
5628     EL_EMC_GATE_8,                      -1, -1
5629   },
5630   {
5631     Xkey_1,                             TRUE,   FALSE,
5632     EL_EM_KEY_1,                        -1, -1
5633   },
5634   {
5635     Xkey_2,                             TRUE,   FALSE,
5636     EL_EM_KEY_2,                        -1, -1
5637   },
5638   {
5639     Xkey_3,                             TRUE,   FALSE,
5640     EL_EM_KEY_3,                        -1, -1
5641   },
5642   {
5643     Xkey_4,                             TRUE,   FALSE,
5644     EL_EM_KEY_4,                        -1, -1
5645   },
5646   {
5647     Xkey_5,                             TRUE,   FALSE,
5648     EL_EMC_KEY_5,                       -1, -1
5649   },
5650   {
5651     Xkey_6,                             TRUE,   FALSE,
5652     EL_EMC_KEY_6,                       -1, -1
5653   },
5654   {
5655     Xkey_7,                             TRUE,   FALSE,
5656     EL_EMC_KEY_7,                       -1, -1
5657   },
5658   {
5659     Xkey_8,                             TRUE,   FALSE,
5660     EL_EMC_KEY_8,                       -1, -1
5661   },
5662   {
5663     Xwind_n,                            TRUE,   FALSE,
5664     EL_BALLOON_SWITCH_UP,               -1, -1
5665   },
5666   {
5667     Xwind_e,                            TRUE,   FALSE,
5668     EL_BALLOON_SWITCH_RIGHT,            -1, -1
5669   },
5670   {
5671     Xwind_s,                            TRUE,   FALSE,
5672     EL_BALLOON_SWITCH_DOWN,             -1, -1
5673   },
5674   {
5675     Xwind_w,                            TRUE,   FALSE,
5676     EL_BALLOON_SWITCH_LEFT,             -1, -1
5677   },
5678   {
5679     Xwind_nesw,                         TRUE,   FALSE,
5680     EL_BALLOON_SWITCH_ANY,              -1, -1
5681   },
5682   {
5683     Xwind_stop,                         TRUE,   FALSE,
5684     EL_BALLOON_SWITCH_NONE,             -1, -1
5685   },
5686   {
5687     Xexit,                              TRUE,   FALSE,
5688     EL_EM_EXIT_CLOSED,                  -1, -1
5689   },
5690   {
5691     Xexit_1,                            TRUE,   FALSE,
5692     EL_EM_EXIT_OPEN,                    -1, -1
5693   },
5694   {
5695     Xexit_2,                            FALSE,  FALSE,
5696     EL_EM_EXIT_OPEN,                    -1, -1
5697   },
5698   {
5699     Xexit_3,                            FALSE,  FALSE,
5700     EL_EM_EXIT_OPEN,                    -1, -1
5701   },
5702   {
5703     Xdynamite,                          TRUE,   FALSE,
5704     EL_EM_DYNAMITE,                     -1, -1
5705   },
5706   {
5707     Ydynamite_eat,                      FALSE,  FALSE,
5708     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
5709   },
5710   {
5711     Xdynamite_1,                        TRUE,   FALSE,
5712     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5713   },
5714   {
5715     Xdynamite_2,                        FALSE,  FALSE,
5716     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5717   },
5718   {
5719     Xdynamite_3,                        FALSE,  FALSE,
5720     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5721   },
5722   {
5723     Xdynamite_4,                        FALSE,  FALSE,
5724     EL_EM_DYNAMITE_ACTIVE,              -1, -1
5725   },
5726   {
5727     Xbumper,                            TRUE,   FALSE,
5728     EL_EMC_SPRING_BUMPER,               -1, -1
5729   },
5730   {
5731     XbumperB,                           FALSE,  FALSE,
5732     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
5733   },
5734   {
5735     Xwheel,                             TRUE,   FALSE,
5736     EL_ROBOT_WHEEL,                     -1, -1
5737   },
5738   {
5739     XwheelB,                            FALSE,  FALSE,
5740     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
5741   },
5742   {
5743     Xswitch,                            TRUE,   FALSE,
5744     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
5745   },
5746   {
5747     XswitchB,                           FALSE,  FALSE,
5748     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
5749   },
5750   {
5751     Xsand,                              TRUE,   FALSE,
5752     EL_QUICKSAND_EMPTY,                 -1, -1
5753   },
5754   {
5755     Xsand_stone,                        TRUE,   FALSE,
5756     EL_QUICKSAND_FULL,                  -1, -1
5757   },
5758   {
5759     Xsand_stonein_1,                    FALSE,  TRUE,
5760     EL_ROCK,                            ACTION_FILLING, -1
5761   },
5762   {
5763     Xsand_stonein_2,                    FALSE,  TRUE,
5764     EL_ROCK,                            ACTION_FILLING, -1
5765   },
5766   {
5767     Xsand_stonein_3,                    FALSE,  TRUE,
5768     EL_ROCK,                            ACTION_FILLING, -1
5769   },
5770   {
5771     Xsand_stonein_4,                    FALSE,  TRUE,
5772     EL_ROCK,                            ACTION_FILLING, -1
5773   },
5774   {
5775     Xsand_stonesand_1,                  FALSE,  FALSE,
5776     EL_QUICKSAND_EMPTYING,              -1, -1
5777   },
5778   {
5779     Xsand_stonesand_2,                  FALSE,  FALSE,
5780     EL_QUICKSAND_EMPTYING,              -1, -1
5781   },
5782   {
5783     Xsand_stonesand_3,                  FALSE,  FALSE,
5784     EL_QUICKSAND_EMPTYING,              -1, -1
5785   },
5786   {
5787     Xsand_stonesand_4,                  FALSE,  FALSE,
5788     EL_QUICKSAND_EMPTYING,              -1, -1
5789   },
5790   {
5791     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
5792     EL_QUICKSAND_EMPTYING,              -1, -1
5793   },
5794   {
5795     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
5796     EL_QUICKSAND_EMPTYING,              -1, -1
5797   },
5798   {
5799     Xsand_stoneout_1,                   FALSE,  FALSE,
5800     EL_ROCK,                            ACTION_EMPTYING, -1
5801   },
5802   {
5803     Xsand_stoneout_2,                   FALSE,  FALSE,
5804     EL_ROCK,                            ACTION_EMPTYING, -1
5805   },
5806   {
5807     Xsand_sandstone_1,                  FALSE,  FALSE,
5808     EL_QUICKSAND_FILLING,               -1, -1
5809   },
5810   {
5811     Xsand_sandstone_2,                  FALSE,  FALSE,
5812     EL_QUICKSAND_FILLING,               -1, -1
5813   },
5814   {
5815     Xsand_sandstone_3,                  FALSE,  FALSE,
5816     EL_QUICKSAND_FILLING,               -1, -1
5817   },
5818   {
5819     Xsand_sandstone_4,                  FALSE,  FALSE,
5820     EL_QUICKSAND_FILLING,               -1, -1
5821   },
5822   {
5823     Xplant,                             TRUE,   FALSE,
5824     EL_EMC_PLANT,                       -1, -1
5825   },
5826   {
5827     Yplant,                             FALSE,  FALSE,
5828     EL_EMC_PLANT,                       -1, -1
5829   },
5830   {
5831     Xlenses,                            TRUE,   FALSE,
5832     EL_EMC_LENSES,                      -1, -1
5833   },
5834   {
5835     Xmagnify,                           TRUE,   FALSE,
5836     EL_EMC_MAGNIFIER,                   -1, -1
5837   },
5838   {
5839     Xdripper,                           TRUE,   FALSE,
5840     EL_EMC_DRIPPER,                     -1, -1
5841   },
5842   {
5843     XdripperB,                          FALSE,  FALSE,
5844     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
5845   },
5846   {
5847     Xfake_blank,                        TRUE,   FALSE,
5848     EL_INVISIBLE_WALL,                  -1, -1
5849   },
5850   {
5851     Xfake_blankB,                       FALSE,  FALSE,
5852     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
5853   },
5854   {
5855     Xfake_grass,                        TRUE,   FALSE,
5856     EL_EMC_FAKE_GRASS,                  -1, -1
5857   },
5858   {
5859     Xfake_grassB,                       FALSE,  FALSE,
5860     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
5861   },
5862   {
5863     Xfake_door_1,                       TRUE,   FALSE,
5864     EL_EM_GATE_1_GRAY,                  -1, -1
5865   },
5866   {
5867     Xfake_door_2,                       TRUE,   FALSE,
5868     EL_EM_GATE_2_GRAY,                  -1, -1
5869   },
5870   {
5871     Xfake_door_3,                       TRUE,   FALSE,
5872     EL_EM_GATE_3_GRAY,                  -1, -1
5873   },
5874   {
5875     Xfake_door_4,                       TRUE,   FALSE,
5876     EL_EM_GATE_4_GRAY,                  -1, -1
5877   },
5878   {
5879     Xfake_door_5,                       TRUE,   FALSE,
5880     EL_EMC_GATE_5_GRAY,                 -1, -1
5881   },
5882   {
5883     Xfake_door_6,                       TRUE,   FALSE,
5884     EL_EMC_GATE_6_GRAY,                 -1, -1
5885   },
5886   {
5887     Xfake_door_7,                       TRUE,   FALSE,
5888     EL_EMC_GATE_7_GRAY,                 -1, -1
5889   },
5890   {
5891     Xfake_door_8,                       TRUE,   FALSE,
5892     EL_EMC_GATE_8_GRAY,                 -1, -1
5893   },
5894   {
5895     Xfake_acid_1,                       TRUE,   FALSE,
5896     EL_EMC_FAKE_ACID,                   -1, -1
5897   },
5898   {
5899     Xfake_acid_2,                       FALSE,  FALSE,
5900     EL_EMC_FAKE_ACID,                   -1, -1
5901   },
5902   {
5903     Xfake_acid_3,                       FALSE,  FALSE,
5904     EL_EMC_FAKE_ACID,                   -1, -1
5905   },
5906   {
5907     Xfake_acid_4,                       FALSE,  FALSE,
5908     EL_EMC_FAKE_ACID,                   -1, -1
5909   },
5910   {
5911     Xfake_acid_5,                       FALSE,  FALSE,
5912     EL_EMC_FAKE_ACID,                   -1, -1
5913   },
5914   {
5915     Xfake_acid_6,                       FALSE,  FALSE,
5916     EL_EMC_FAKE_ACID,                   -1, -1
5917   },
5918   {
5919     Xfake_acid_7,                       FALSE,  FALSE,
5920     EL_EMC_FAKE_ACID,                   -1, -1
5921   },
5922   {
5923     Xfake_acid_8,                       FALSE,  FALSE,
5924     EL_EMC_FAKE_ACID,                   -1, -1
5925   },
5926   {
5927     Xsteel_1,                           TRUE,   FALSE,
5928     EL_STEELWALL,                       -1, -1
5929   },
5930   {
5931     Xsteel_2,                           TRUE,   FALSE,
5932     EL_EMC_STEELWALL_2,                 -1, -1
5933   },
5934   {
5935     Xsteel_3,                           TRUE,   FALSE,
5936     EL_EMC_STEELWALL_3,                 -1, -1
5937   },
5938   {
5939     Xsteel_4,                           TRUE,   FALSE,
5940     EL_EMC_STEELWALL_4,                 -1, -1
5941   },
5942   {
5943     Xwall_1,                            TRUE,   FALSE,
5944     EL_WALL,                            -1, -1
5945   },
5946   {
5947     Xwall_2,                            TRUE,   FALSE,
5948     EL_EMC_WALL_14,                     -1, -1
5949   },
5950   {
5951     Xwall_3,                            TRUE,   FALSE,
5952     EL_EMC_WALL_15,                     -1, -1
5953   },
5954   {
5955     Xwall_4,                            TRUE,   FALSE,
5956     EL_EMC_WALL_16,                     -1, -1
5957   },
5958   {
5959     Xround_wall_1,                      TRUE,   FALSE,
5960     EL_WALL_SLIPPERY,                   -1, -1
5961   },
5962   {
5963     Xround_wall_2,                      TRUE,   FALSE,
5964     EL_EMC_WALL_SLIPPERY_2,             -1, -1
5965   },
5966   {
5967     Xround_wall_3,                      TRUE,   FALSE,
5968     EL_EMC_WALL_SLIPPERY_3,             -1, -1
5969   },
5970   {
5971     Xround_wall_4,                      TRUE,   FALSE,
5972     EL_EMC_WALL_SLIPPERY_4,             -1, -1
5973   },
5974   {
5975     Xdecor_1,                           TRUE,   FALSE,
5976     EL_EMC_WALL_8,                      -1, -1
5977   },
5978   {
5979     Xdecor_2,                           TRUE,   FALSE,
5980     EL_EMC_WALL_6,                      -1, -1
5981   },
5982   {
5983     Xdecor_3,                           TRUE,   FALSE,
5984     EL_EMC_WALL_4,                      -1, -1
5985   },
5986   {
5987     Xdecor_4,                           TRUE,   FALSE,
5988     EL_EMC_WALL_7,                      -1, -1
5989   },
5990   {
5991     Xdecor_5,                           TRUE,   FALSE,
5992     EL_EMC_WALL_5,                      -1, -1
5993   },
5994   {
5995     Xdecor_6,                           TRUE,   FALSE,
5996     EL_EMC_WALL_9,                      -1, -1
5997   },
5998   {
5999     Xdecor_7,                           TRUE,   FALSE,
6000     EL_EMC_WALL_10,                     -1, -1
6001   },
6002   {
6003     Xdecor_8,                           TRUE,   FALSE,
6004     EL_EMC_WALL_1,                      -1, -1
6005   },
6006   {
6007     Xdecor_9,                           TRUE,   FALSE,
6008     EL_EMC_WALL_2,                      -1, -1
6009   },
6010   {
6011     Xdecor_10,                          TRUE,   FALSE,
6012     EL_EMC_WALL_3,                      -1, -1
6013   },
6014   {
6015     Xdecor_11,                          TRUE,   FALSE,
6016     EL_EMC_WALL_11,                     -1, -1
6017   },
6018   {
6019     Xdecor_12,                          TRUE,   FALSE,
6020     EL_EMC_WALL_12,                     -1, -1
6021   },
6022   {
6023     Xalpha_0,                           TRUE,   FALSE,
6024     EL_CHAR('0'),                       -1, -1
6025   },
6026   {
6027     Xalpha_1,                           TRUE,   FALSE,
6028     EL_CHAR('1'),                       -1, -1
6029   },
6030   {
6031     Xalpha_2,                           TRUE,   FALSE,
6032     EL_CHAR('2'),                       -1, -1
6033   },
6034   {
6035     Xalpha_3,                           TRUE,   FALSE,
6036     EL_CHAR('3'),                       -1, -1
6037   },
6038   {
6039     Xalpha_4,                           TRUE,   FALSE,
6040     EL_CHAR('4'),                       -1, -1
6041   },
6042   {
6043     Xalpha_5,                           TRUE,   FALSE,
6044     EL_CHAR('5'),                       -1, -1
6045   },
6046   {
6047     Xalpha_6,                           TRUE,   FALSE,
6048     EL_CHAR('6'),                       -1, -1
6049   },
6050   {
6051     Xalpha_7,                           TRUE,   FALSE,
6052     EL_CHAR('7'),                       -1, -1
6053   },
6054   {
6055     Xalpha_8,                           TRUE,   FALSE,
6056     EL_CHAR('8'),                       -1, -1
6057   },
6058   {
6059     Xalpha_9,                           TRUE,   FALSE,
6060     EL_CHAR('9'),                       -1, -1
6061   },
6062   {
6063     Xalpha_excla,                       TRUE,   FALSE,
6064     EL_CHAR('!'),                       -1, -1
6065   },
6066   {
6067     Xalpha_quote,                       TRUE,   FALSE,
6068     EL_CHAR('"'),                       -1, -1
6069   },
6070   {
6071     Xalpha_comma,                       TRUE,   FALSE,
6072     EL_CHAR(','),                       -1, -1
6073   },
6074   {
6075     Xalpha_minus,                       TRUE,   FALSE,
6076     EL_CHAR('-'),                       -1, -1
6077   },
6078   {
6079     Xalpha_perio,                       TRUE,   FALSE,
6080     EL_CHAR('.'),                       -1, -1
6081   },
6082   {
6083     Xalpha_colon,                       TRUE,   FALSE,
6084     EL_CHAR(':'),                       -1, -1
6085   },
6086   {
6087     Xalpha_quest,                       TRUE,   FALSE,
6088     EL_CHAR('?'),                       -1, -1
6089   },
6090   {
6091     Xalpha_a,                           TRUE,   FALSE,
6092     EL_CHAR('A'),                       -1, -1
6093   },
6094   {
6095     Xalpha_b,                           TRUE,   FALSE,
6096     EL_CHAR('B'),                       -1, -1
6097   },
6098   {
6099     Xalpha_c,                           TRUE,   FALSE,
6100     EL_CHAR('C'),                       -1, -1
6101   },
6102   {
6103     Xalpha_d,                           TRUE,   FALSE,
6104     EL_CHAR('D'),                       -1, -1
6105   },
6106   {
6107     Xalpha_e,                           TRUE,   FALSE,
6108     EL_CHAR('E'),                       -1, -1
6109   },
6110   {
6111     Xalpha_f,                           TRUE,   FALSE,
6112     EL_CHAR('F'),                       -1, -1
6113   },
6114   {
6115     Xalpha_g,                           TRUE,   FALSE,
6116     EL_CHAR('G'),                       -1, -1
6117   },
6118   {
6119     Xalpha_h,                           TRUE,   FALSE,
6120     EL_CHAR('H'),                       -1, -1
6121   },
6122   {
6123     Xalpha_i,                           TRUE,   FALSE,
6124     EL_CHAR('I'),                       -1, -1
6125   },
6126   {
6127     Xalpha_j,                           TRUE,   FALSE,
6128     EL_CHAR('J'),                       -1, -1
6129   },
6130   {
6131     Xalpha_k,                           TRUE,   FALSE,
6132     EL_CHAR('K'),                       -1, -1
6133   },
6134   {
6135     Xalpha_l,                           TRUE,   FALSE,
6136     EL_CHAR('L'),                       -1, -1
6137   },
6138   {
6139     Xalpha_m,                           TRUE,   FALSE,
6140     EL_CHAR('M'),                       -1, -1
6141   },
6142   {
6143     Xalpha_n,                           TRUE,   FALSE,
6144     EL_CHAR('N'),                       -1, -1
6145   },
6146   {
6147     Xalpha_o,                           TRUE,   FALSE,
6148     EL_CHAR('O'),                       -1, -1
6149   },
6150   {
6151     Xalpha_p,                           TRUE,   FALSE,
6152     EL_CHAR('P'),                       -1, -1
6153   },
6154   {
6155     Xalpha_q,                           TRUE,   FALSE,
6156     EL_CHAR('Q'),                       -1, -1
6157   },
6158   {
6159     Xalpha_r,                           TRUE,   FALSE,
6160     EL_CHAR('R'),                       -1, -1
6161   },
6162   {
6163     Xalpha_s,                           TRUE,   FALSE,
6164     EL_CHAR('S'),                       -1, -1
6165   },
6166   {
6167     Xalpha_t,                           TRUE,   FALSE,
6168     EL_CHAR('T'),                       -1, -1
6169   },
6170   {
6171     Xalpha_u,                           TRUE,   FALSE,
6172     EL_CHAR('U'),                       -1, -1
6173   },
6174   {
6175     Xalpha_v,                           TRUE,   FALSE,
6176     EL_CHAR('V'),                       -1, -1
6177   },
6178   {
6179     Xalpha_w,                           TRUE,   FALSE,
6180     EL_CHAR('W'),                       -1, -1
6181   },
6182   {
6183     Xalpha_x,                           TRUE,   FALSE,
6184     EL_CHAR('X'),                       -1, -1
6185   },
6186   {
6187     Xalpha_y,                           TRUE,   FALSE,
6188     EL_CHAR('Y'),                       -1, -1
6189   },
6190   {
6191     Xalpha_z,                           TRUE,   FALSE,
6192     EL_CHAR('Z'),                       -1, -1
6193   },
6194   {
6195     Xalpha_arrow_e,                     TRUE,   FALSE,
6196     EL_CHAR('>'),                       -1, -1
6197   },
6198   {
6199     Xalpha_arrow_w,                     TRUE,   FALSE,
6200     EL_CHAR('<'),                       -1, -1
6201   },
6202   {
6203     Xalpha_copyr,                       TRUE,   FALSE,
6204     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
6205   },
6206
6207   {
6208     Xboom_bug,                          FALSE,  FALSE,
6209     EL_BUG,                             ACTION_EXPLODING, -1
6210   },
6211   {
6212     Xboom_bomb,                         FALSE,  FALSE,
6213     EL_BOMB,                            ACTION_EXPLODING, -1
6214   },
6215   {
6216     Xboom_android,                      FALSE,  FALSE,
6217     EL_EMC_ANDROID,                     ACTION_OTHER, -1
6218   },
6219   {
6220     Xboom_1,                            FALSE,  FALSE,
6221     EL_DEFAULT,                         ACTION_EXPLODING, -1
6222   },
6223   {
6224     Xboom_2,                            FALSE,  FALSE,
6225     EL_DEFAULT,                         ACTION_EXPLODING, -1
6226   },
6227   {
6228     Znormal,                            FALSE,  FALSE,
6229     EL_EMPTY,                           -1, -1
6230   },
6231   {
6232     Zdynamite,                          FALSE,  FALSE,
6233     EL_EMPTY,                           -1, -1
6234   },
6235   {
6236     Zplayer,                            FALSE,  FALSE,
6237     EL_EMPTY,                           -1, -1
6238   },
6239   {
6240     ZBORDER,                            FALSE,  FALSE,
6241     EL_EMPTY,                           -1, -1
6242   },
6243
6244   {
6245     -1,                                 FALSE,  FALSE,
6246     -1,                                 -1, -1
6247   }
6248 };
6249
6250 static struct Mapping_EM_to_RND_player
6251 {
6252   int action_em;
6253   int player_nr;
6254
6255   int element_rnd;
6256   int action;
6257   int direction;
6258 }
6259 em_player_mapping_list[] =
6260 {
6261   {
6262     SPR_walk + 0,                       0,
6263     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
6264   },
6265   {
6266     SPR_walk + 1,                       0,
6267     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
6268   },
6269   {
6270     SPR_walk + 2,                       0,
6271     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
6272   },
6273   {
6274     SPR_walk + 3,                       0,
6275     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
6276   },
6277   {
6278     SPR_push + 0,                       0,
6279     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
6280   },
6281   {
6282     SPR_push + 1,                       0,
6283     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
6284   },
6285   {
6286     SPR_push + 2,                       0,
6287     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
6288   },
6289   {
6290     SPR_push + 3,                       0,
6291     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
6292   },
6293   {
6294     SPR_spray + 0,                      0,
6295     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
6296   },
6297   {
6298     SPR_spray + 1,                      0,
6299     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6300   },
6301   {
6302     SPR_spray + 2,                      0,
6303     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
6304   },
6305   {
6306     SPR_spray + 3,                      0,
6307     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
6308   },
6309   {
6310     SPR_walk + 0,                       1,
6311     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
6312   },
6313   {
6314     SPR_walk + 1,                       1,
6315     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
6316   },
6317   {
6318     SPR_walk + 2,                       1,
6319     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
6320   },
6321   {
6322     SPR_walk + 3,                       1,
6323     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
6324   },
6325   {
6326     SPR_push + 0,                       1,
6327     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
6328   },
6329   {
6330     SPR_push + 1,                       1,
6331     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
6332   },
6333   {
6334     SPR_push + 2,                       1,
6335     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
6336   },
6337   {
6338     SPR_push + 3,                       1,
6339     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
6340   },
6341   {
6342     SPR_spray + 0,                      1,
6343     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
6344   },
6345   {
6346     SPR_spray + 1,                      1,
6347     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6348   },
6349   {
6350     SPR_spray + 2,                      1,
6351     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
6352   },
6353   {
6354     SPR_spray + 3,                      1,
6355     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
6356   },
6357   {
6358     SPR_still,                          0,
6359     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
6360   },
6361   {
6362     SPR_still,                          1,
6363     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
6364   },
6365   {
6366     SPR_walk + 0,                       2,
6367     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
6368   },
6369   {
6370     SPR_walk + 1,                       2,
6371     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
6372   },
6373   {
6374     SPR_walk + 2,                       2,
6375     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
6376   },
6377   {
6378     SPR_walk + 3,                       2,
6379     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
6380   },
6381   {
6382     SPR_push + 0,                       2,
6383     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
6384   },
6385   {
6386     SPR_push + 1,                       2,
6387     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
6388   },
6389   {
6390     SPR_push + 2,                       2,
6391     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
6392   },
6393   {
6394     SPR_push + 3,                       2,
6395     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
6396   },
6397   {
6398     SPR_spray + 0,                      2,
6399     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
6400   },
6401   {
6402     SPR_spray + 1,                      2,
6403     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6404   },
6405   {
6406     SPR_spray + 2,                      2,
6407     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
6408   },
6409   {
6410     SPR_spray + 3,                      2,
6411     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
6412   },
6413   {
6414     SPR_walk + 0,                       3,
6415     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
6416   },
6417   {
6418     SPR_walk + 1,                       3,
6419     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
6420   },
6421   {
6422     SPR_walk + 2,                       3,
6423     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
6424   },
6425   {
6426     SPR_walk + 3,                       3,
6427     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
6428   },
6429   {
6430     SPR_push + 0,                       3,
6431     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
6432   },
6433   {
6434     SPR_push + 1,                       3,
6435     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
6436   },
6437   {
6438     SPR_push + 2,                       3,
6439     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
6440   },
6441   {
6442     SPR_push + 3,                       3,
6443     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
6444   },
6445   {
6446     SPR_spray + 0,                      3,
6447     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
6448   },
6449   {
6450     SPR_spray + 1,                      3,
6451     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6452   },
6453   {
6454     SPR_spray + 2,                      3,
6455     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
6456   },
6457   {
6458     SPR_spray + 3,                      3,
6459     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
6460   },
6461   {
6462     SPR_still,                          2,
6463     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
6464   },
6465   {
6466     SPR_still,                          3,
6467     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
6468   },
6469
6470   {
6471     -1,                                 -1,
6472     -1,                                 -1, -1
6473   }
6474 };
6475
6476 int map_element_RND_to_EM(int element_rnd)
6477 {
6478   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6479   static boolean mapping_initialized = FALSE;
6480
6481   if (!mapping_initialized)
6482   {
6483     int i;
6484
6485     /* return "Xalpha_quest" for all undefined elements in mapping array */
6486     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6487       mapping_RND_to_EM[i] = Xalpha_quest;
6488
6489     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6490       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6491         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6492           em_object_mapping_list[i].element_em;
6493
6494     mapping_initialized = TRUE;
6495   }
6496
6497   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6498     return mapping_RND_to_EM[element_rnd];
6499
6500   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6501
6502   return EL_UNKNOWN;
6503 }
6504
6505 int map_element_EM_to_RND(int element_em)
6506 {
6507   static unsigned short mapping_EM_to_RND[TILE_MAX];
6508   static boolean mapping_initialized = FALSE;
6509
6510   if (!mapping_initialized)
6511   {
6512     int i;
6513
6514     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6515     for (i = 0; i < TILE_MAX; i++)
6516       mapping_EM_to_RND[i] = EL_UNKNOWN;
6517
6518     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6519       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6520         em_object_mapping_list[i].element_rnd;
6521
6522     mapping_initialized = TRUE;
6523   }
6524
6525   if (element_em >= 0 && element_em < TILE_MAX)
6526     return mapping_EM_to_RND[element_em];
6527
6528   Error(ERR_WARN, "invalid EM level element %d", element_em);
6529
6530   return EL_UNKNOWN;
6531 }
6532
6533 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6534 {
6535   struct LevelInfo_EM *level_em = level->native_em_level;
6536   struct LEVEL *lev = level_em->lev;
6537   int i, j;
6538
6539   for (i = 0; i < TILE_MAX; i++)
6540     lev->android_array[i] = Xblank;
6541
6542   for (i = 0; i < level->num_android_clone_elements; i++)
6543   {
6544     int element_rnd = level->android_clone_element[i];
6545     int element_em = map_element_RND_to_EM(element_rnd);
6546
6547     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6548       if (em_object_mapping_list[j].element_rnd == element_rnd)
6549         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6550   }
6551 }
6552
6553 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6554 {
6555   struct LevelInfo_EM *level_em = level->native_em_level;
6556   struct LEVEL *lev = level_em->lev;
6557   int i, j;
6558
6559   level->num_android_clone_elements = 0;
6560
6561   for (i = 0; i < TILE_MAX; i++)
6562   {
6563     int element_em = lev->android_array[i];
6564     int element_rnd;
6565     boolean element_found = FALSE;
6566
6567     if (element_em == Xblank)
6568       continue;
6569
6570     element_rnd = map_element_EM_to_RND(element_em);
6571
6572     for (j = 0; j < level->num_android_clone_elements; j++)
6573       if (level->android_clone_element[j] == element_rnd)
6574         element_found = TRUE;
6575
6576     if (!element_found)
6577     {
6578       level->android_clone_element[level->num_android_clone_elements++] =
6579         element_rnd;
6580
6581       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6582         break;
6583     }
6584   }
6585
6586   if (level->num_android_clone_elements == 0)
6587   {
6588     level->num_android_clone_elements = 1;
6589     level->android_clone_element[0] = EL_EMPTY;
6590   }
6591 }
6592
6593 int map_direction_RND_to_EM(int direction)
6594 {
6595   return (direction == MV_UP    ? 0 :
6596           direction == MV_RIGHT ? 1 :
6597           direction == MV_DOWN  ? 2 :
6598           direction == MV_LEFT  ? 3 :
6599           -1);
6600 }
6601
6602 int map_direction_EM_to_RND(int direction)
6603 {
6604   return (direction == 0 ? MV_UP    :
6605           direction == 1 ? MV_RIGHT :
6606           direction == 2 ? MV_DOWN  :
6607           direction == 3 ? MV_LEFT  :
6608           MV_NONE);
6609 }
6610
6611 int map_element_RND_to_SP(int element_rnd)
6612 {
6613   int element_sp = 0x20;        /* map unknown elements to yellow "hardware" */
6614
6615   if (element_rnd >= EL_SP_START &&
6616       element_rnd <= EL_SP_END)
6617     element_sp = element_rnd - EL_SP_START;
6618   else if (element_rnd == EL_EMPTY_SPACE)
6619     element_sp = 0x00;
6620   else if (element_rnd == EL_INVISIBLE_WALL)
6621     element_sp = 0x28;
6622
6623   return element_sp;
6624 }
6625
6626 int map_element_SP_to_RND(int element_sp)
6627 {
6628   int element_rnd = EL_UNKNOWN;
6629
6630   if (element_sp >= 0x00 &&
6631       element_sp <= 0x27)
6632     element_rnd = EL_SP_START + element_sp;
6633   else if (element_sp == 0x28)
6634     element_rnd = EL_INVISIBLE_WALL;
6635
6636   return element_rnd;
6637 }
6638
6639 int map_action_SP_to_RND(int action_sp)
6640 {
6641   switch (action_sp)
6642   {
6643     case actActive:             return ACTION_ACTIVE;
6644     case actImpact:             return ACTION_IMPACT;
6645     case actExploding:          return ACTION_EXPLODING;
6646     case actDigging:            return ACTION_DIGGING;
6647     case actSnapping:           return ACTION_SNAPPING;
6648     case actCollecting:         return ACTION_COLLECTING;
6649     case actPassing:            return ACTION_PASSING;
6650     case actPushing:            return ACTION_PUSHING;
6651     case actDropping:           return ACTION_DROPPING;
6652
6653     default:                    return ACTION_DEFAULT;
6654   }
6655 }
6656
6657 int get_next_element(int element)
6658 {
6659   switch (element)
6660   {
6661     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
6662     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
6663     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
6664     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
6665     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
6666     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
6667     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
6668     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
6669     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
6670     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
6671     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
6672
6673     default:                            return element;
6674   }
6675 }
6676
6677 int el_act_dir2img(int element, int action, int direction)
6678 {
6679   element = GFX_ELEMENT(element);
6680   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6681
6682   /* direction_graphic[][] == graphic[] for undefined direction graphics */
6683   return element_info[element].direction_graphic[action][direction];
6684 }
6685
6686 static int el_act_dir2crm(int element, int action, int direction)
6687 {
6688   element = GFX_ELEMENT(element);
6689   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6690
6691   /* direction_graphic[][] == graphic[] for undefined direction graphics */
6692   return element_info[element].direction_crumbled[action][direction];
6693 }
6694
6695 int el_act2img(int element, int action)
6696 {
6697   element = GFX_ELEMENT(element);
6698
6699   return element_info[element].graphic[action];
6700 }
6701
6702 int el_act2crm(int element, int action)
6703 {
6704   element = GFX_ELEMENT(element);
6705
6706   return element_info[element].crumbled[action];
6707 }
6708
6709 int el_dir2img(int element, int direction)
6710 {
6711   element = GFX_ELEMENT(element);
6712
6713   return el_act_dir2img(element, ACTION_DEFAULT, direction);
6714 }
6715
6716 int el2baseimg(int element)
6717 {
6718   return element_info[element].graphic[ACTION_DEFAULT];
6719 }
6720
6721 int el2img(int element)
6722 {
6723   element = GFX_ELEMENT(element);
6724
6725   return element_info[element].graphic[ACTION_DEFAULT];
6726 }
6727
6728 int el2edimg(int element)
6729 {
6730   element = GFX_ELEMENT(element);
6731
6732   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6733 }
6734
6735 int el2preimg(int element)
6736 {
6737   element = GFX_ELEMENT(element);
6738
6739   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6740 }
6741
6742 int el2panelimg(int element)
6743 {
6744   element = GFX_ELEMENT(element);
6745
6746   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6747 }
6748
6749 int font2baseimg(int font_nr)
6750 {
6751   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6752 }
6753
6754 int getBeltNrFromBeltElement(int element)
6755 {
6756   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6757           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6758           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6759 }
6760
6761 int getBeltNrFromBeltActiveElement(int element)
6762 {
6763   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6764           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6765           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6766 }
6767
6768 int getBeltNrFromBeltSwitchElement(int element)
6769 {
6770   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6771           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6772           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6773 }
6774
6775 int getBeltDirNrFromBeltElement(int element)
6776 {
6777   static int belt_base_element[4] =
6778   {
6779     EL_CONVEYOR_BELT_1_LEFT,
6780     EL_CONVEYOR_BELT_2_LEFT,
6781     EL_CONVEYOR_BELT_3_LEFT,
6782     EL_CONVEYOR_BELT_4_LEFT
6783   };
6784
6785   int belt_nr = getBeltNrFromBeltElement(element);
6786   int belt_dir_nr = element - belt_base_element[belt_nr];
6787
6788   return (belt_dir_nr % 3);
6789 }
6790
6791 int getBeltDirNrFromBeltSwitchElement(int element)
6792 {
6793   static int belt_base_element[4] =
6794   {
6795     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6796     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6797     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6798     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6799   };
6800
6801   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6802   int belt_dir_nr = element - belt_base_element[belt_nr];
6803
6804   return (belt_dir_nr % 3);
6805 }
6806
6807 int getBeltDirFromBeltElement(int element)
6808 {
6809   static int belt_move_dir[3] =
6810   {
6811     MV_LEFT,
6812     MV_NONE,
6813     MV_RIGHT
6814   };
6815
6816   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6817
6818   return belt_move_dir[belt_dir_nr];
6819 }
6820
6821 int getBeltDirFromBeltSwitchElement(int element)
6822 {
6823   static int belt_move_dir[3] =
6824   {
6825     MV_LEFT,
6826     MV_NONE,
6827     MV_RIGHT
6828   };
6829
6830   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6831
6832   return belt_move_dir[belt_dir_nr];
6833 }
6834
6835 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6836 {
6837   static int belt_base_element[4] =
6838   {
6839     EL_CONVEYOR_BELT_1_LEFT,
6840     EL_CONVEYOR_BELT_2_LEFT,
6841     EL_CONVEYOR_BELT_3_LEFT,
6842     EL_CONVEYOR_BELT_4_LEFT
6843   };
6844
6845   return belt_base_element[belt_nr] + belt_dir_nr;
6846 }
6847
6848 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6849 {
6850   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6851
6852   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6853 }
6854
6855 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6856 {
6857   static int belt_base_element[4] =
6858   {
6859     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6860     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6861     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6862     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6863   };
6864
6865   return belt_base_element[belt_nr] + belt_dir_nr;
6866 }
6867
6868 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6869 {
6870   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6871
6872   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6873 }
6874
6875 boolean getTeamMode_EM()
6876 {
6877   return game.team_mode;
6878 }
6879
6880 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6881 {
6882   int game_frame_delay_value;
6883
6884   game_frame_delay_value =
6885     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6886      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6887      GameFrameDelay);
6888
6889   if (tape.playing && tape.warp_forward && !tape.pausing)
6890     game_frame_delay_value = 0;
6891
6892   return game_frame_delay_value;
6893 }
6894
6895 unsigned int InitRND(int seed)
6896 {
6897   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6898     return InitEngineRandom_EM(seed);
6899   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6900     return InitEngineRandom_SP(seed);
6901   else
6902     return InitEngineRandom_RND(seed);
6903 }
6904
6905 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6906 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6907
6908 inline static int get_effective_element_EM(int tile, int frame_em)
6909 {
6910   int element             = object_mapping[tile].element_rnd;
6911   int action              = object_mapping[tile].action;
6912   boolean is_backside     = object_mapping[tile].is_backside;
6913   boolean action_removing = (action == ACTION_DIGGING ||
6914                              action == ACTION_SNAPPING ||
6915                              action == ACTION_COLLECTING);
6916
6917   if (frame_em < 7)
6918   {
6919     switch (tile)
6920     {
6921       case Yacid_splash_eB:
6922       case Yacid_splash_wB:
6923         return (frame_em > 5 ? EL_EMPTY : element);
6924
6925       default:
6926         return element;
6927     }
6928   }
6929   else  /* frame_em == 7 */
6930   {
6931     switch (tile)
6932     {
6933       case Yacid_splash_eB:
6934       case Yacid_splash_wB:
6935         return EL_EMPTY;
6936
6937       case Yemerald_stone:
6938         return EL_EMERALD;
6939
6940       case Ydiamond_stone:
6941         return EL_ROCK;
6942
6943       case Xdrip_stretch:
6944       case Xdrip_stretchB:
6945       case Ydrip_s1:
6946       case Ydrip_s1B:
6947       case Xball_1B:
6948       case Xball_2:
6949       case Xball_2B:
6950       case Yball_eat:
6951       case Ykey_1_eat:
6952       case Ykey_2_eat:
6953       case Ykey_3_eat:
6954       case Ykey_4_eat:
6955       case Ykey_5_eat:
6956       case Ykey_6_eat:
6957       case Ykey_7_eat:
6958       case Ykey_8_eat:
6959       case Ylenses_eat:
6960       case Ymagnify_eat:
6961       case Ygrass_eat:
6962       case Ydirt_eat:
6963       case Xsand_stonein_1:
6964       case Xsand_stonein_2:
6965       case Xsand_stonein_3:
6966       case Xsand_stonein_4:
6967         return element;
6968
6969       default:
6970         return (is_backside || action_removing ? EL_EMPTY : element);
6971     }
6972   }
6973 }
6974
6975 inline static boolean check_linear_animation_EM(int tile)
6976 {
6977   switch (tile)
6978   {
6979     case Xsand_stonesand_1:
6980     case Xsand_stonesand_quickout_1:
6981     case Xsand_sandstone_1:
6982     case Xsand_stonein_1:
6983     case Xsand_stoneout_1:
6984     case Xboom_1:
6985     case Xdynamite_1:
6986     case Ybug_w_n:
6987     case Ybug_n_e:
6988     case Ybug_e_s:
6989     case Ybug_s_w:
6990     case Ybug_e_n:
6991     case Ybug_s_e:
6992     case Ybug_w_s:
6993     case Ybug_n_w:
6994     case Ytank_w_n:
6995     case Ytank_n_e:
6996     case Ytank_e_s:
6997     case Ytank_s_w:
6998     case Ytank_e_n:
6999     case Ytank_s_e:
7000     case Ytank_w_s:
7001     case Ytank_n_w:
7002     case Yacid_splash_eB:
7003     case Yacid_splash_wB:
7004     case Yemerald_stone:
7005       return TRUE;
7006   }
7007
7008   return FALSE;
7009 }
7010
7011 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7012                                             boolean has_crumbled_graphics,
7013                                             int crumbled, int sync_frame)
7014 {
7015   /* if element can be crumbled, but certain action graphics are just empty
7016      space (like instantly snapping sand to empty space in 1 frame), do not
7017      treat these empty space graphics as crumbled graphics in EMC engine */
7018   if (crumbled == IMG_EMPTY_SPACE)
7019     has_crumbled_graphics = FALSE;
7020
7021   if (has_crumbled_graphics)
7022   {
7023     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7024     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7025                                            g_crumbled->anim_delay,
7026                                            g_crumbled->anim_mode,
7027                                            g_crumbled->anim_start_frame,
7028                                            sync_frame);
7029
7030     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7031                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7032
7033     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7034
7035     g_em->has_crumbled_graphics = TRUE;
7036   }
7037   else
7038   {
7039     g_em->crumbled_bitmap = NULL;
7040     g_em->crumbled_src_x = 0;
7041     g_em->crumbled_src_y = 0;
7042     g_em->crumbled_border_size = 0;
7043
7044     g_em->has_crumbled_graphics = FALSE;
7045   }
7046 }
7047
7048 void ResetGfxAnimation_EM(int x, int y, int tile)
7049 {
7050   GfxFrame[x][y] = 0;
7051 }
7052
7053 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7054                         int tile, int frame_em, int x, int y)
7055 {
7056   int action = object_mapping[tile].action;
7057   int direction = object_mapping[tile].direction;
7058   int effective_element = get_effective_element_EM(tile, frame_em);
7059   int graphic = (direction == MV_NONE ?
7060                  el_act2img(effective_element, action) :
7061                  el_act_dir2img(effective_element, action, direction));
7062   struct GraphicInfo *g = &graphic_info[graphic];
7063   int sync_frame;
7064   boolean action_removing = (action == ACTION_DIGGING ||
7065                              action == ACTION_SNAPPING ||
7066                              action == ACTION_COLLECTING);
7067   boolean action_moving   = (action == ACTION_FALLING ||
7068                              action == ACTION_MOVING ||
7069                              action == ACTION_PUSHING ||
7070                              action == ACTION_EATING ||
7071                              action == ACTION_FILLING ||
7072                              action == ACTION_EMPTYING);
7073   boolean action_falling  = (action == ACTION_FALLING ||
7074                              action == ACTION_FILLING ||
7075                              action == ACTION_EMPTYING);
7076
7077   /* special case: graphic uses "2nd movement tile" and has defined
7078      7 frames for movement animation (or less) => use default graphic
7079      for last (8th) frame which ends the movement animation */
7080   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7081   {
7082     action = ACTION_DEFAULT;    /* (keep action_* unchanged for now) */
7083     graphic = (direction == MV_NONE ?
7084                el_act2img(effective_element, action) :
7085                el_act_dir2img(effective_element, action, direction));
7086
7087     g = &graphic_info[graphic];
7088   }
7089
7090   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7091   {
7092     GfxFrame[x][y] = 0;
7093   }
7094   else if (action_moving)
7095   {
7096     boolean is_backside = object_mapping[tile].is_backside;
7097
7098     if (is_backside)
7099     {
7100       int direction = object_mapping[tile].direction;
7101       int move_dir = (action_falling ? MV_DOWN : direction);
7102
7103       GfxFrame[x][y]++;
7104
7105 #if 1
7106       /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7107       if (g->double_movement && frame_em == 0)
7108         GfxFrame[x][y] = 0;
7109 #endif
7110
7111       if (move_dir == MV_LEFT)
7112         GfxFrame[x - 1][y] = GfxFrame[x][y];
7113       else if (move_dir == MV_RIGHT)
7114         GfxFrame[x + 1][y] = GfxFrame[x][y];
7115       else if (move_dir == MV_UP)
7116         GfxFrame[x][y - 1] = GfxFrame[x][y];
7117       else if (move_dir == MV_DOWN)
7118         GfxFrame[x][y + 1] = GfxFrame[x][y];
7119     }
7120   }
7121   else
7122   {
7123     GfxFrame[x][y]++;
7124
7125     /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7126     if (tile == Xsand_stonesand_quickout_1 ||
7127         tile == Xsand_stonesand_quickout_2)
7128       GfxFrame[x][y]++;
7129   }
7130
7131   if (graphic_info[graphic].anim_global_sync)
7132     sync_frame = FrameCounter;
7133   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7134     sync_frame = GfxFrame[x][y];
7135   else
7136     sync_frame = 0;     /* playfield border (pseudo steel) */
7137
7138   SetRandomAnimationValue(x, y);
7139
7140   int frame = getAnimationFrame(g->anim_frames,
7141                                 g->anim_delay,
7142                                 g->anim_mode,
7143                                 g->anim_start_frame,
7144                                 sync_frame);
7145
7146   g_em->unique_identifier =
7147     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7148 }
7149
7150 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7151                                   int tile, int frame_em, int x, int y)
7152 {
7153   int action = object_mapping[tile].action;
7154   int direction = object_mapping[tile].direction;
7155   boolean is_backside = object_mapping[tile].is_backside;
7156   int effective_element = get_effective_element_EM(tile, frame_em);
7157   int effective_action = action;
7158   int graphic = (direction == MV_NONE ?
7159                  el_act2img(effective_element, effective_action) :
7160                  el_act_dir2img(effective_element, effective_action,
7161                                 direction));
7162   int crumbled = (direction == MV_NONE ?
7163                   el_act2crm(effective_element, effective_action) :
7164                   el_act_dir2crm(effective_element, effective_action,
7165                                  direction));
7166   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7167   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7168   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7169   struct GraphicInfo *g = &graphic_info[graphic];
7170   int sync_frame;
7171
7172   /* special case: graphic uses "2nd movement tile" and has defined
7173      7 frames for movement animation (or less) => use default graphic
7174      for last (8th) frame which ends the movement animation */
7175   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7176   {
7177     effective_action = ACTION_DEFAULT;
7178     graphic = (direction == MV_NONE ?
7179                el_act2img(effective_element, effective_action) :
7180                el_act_dir2img(effective_element, effective_action,
7181                               direction));
7182     crumbled = (direction == MV_NONE ?
7183                 el_act2crm(effective_element, effective_action) :
7184                 el_act_dir2crm(effective_element, effective_action,
7185                                direction));
7186
7187     g = &graphic_info[graphic];
7188   }
7189
7190   if (graphic_info[graphic].anim_global_sync)
7191     sync_frame = FrameCounter;
7192   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7193     sync_frame = GfxFrame[x][y];
7194   else
7195     sync_frame = 0;     /* playfield border (pseudo steel) */
7196
7197   SetRandomAnimationValue(x, y);
7198
7199   int frame = getAnimationFrame(g->anim_frames,
7200                                 g->anim_delay,
7201                                 g->anim_mode,
7202                                 g->anim_start_frame,
7203                                 sync_frame);
7204
7205   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7206                       g->double_movement && is_backside);
7207
7208   /* (updating the "crumbled" graphic definitions is probably not really needed,
7209      as animations for crumbled graphics can't be longer than one EMC cycle) */
7210   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7211                            sync_frame);
7212 }
7213
7214 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7215                                   int player_nr, int anim, int frame_em)
7216 {
7217   int element   = player_mapping[player_nr][anim].element_rnd;
7218   int action    = player_mapping[player_nr][anim].action;
7219   int direction = player_mapping[player_nr][anim].direction;
7220   int graphic = (direction == MV_NONE ?
7221                  el_act2img(element, action) :
7222                  el_act_dir2img(element, action, direction));
7223   struct GraphicInfo *g = &graphic_info[graphic];
7224   int sync_frame;
7225
7226   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7227
7228   stored_player[player_nr].StepFrame = frame_em;
7229
7230   sync_frame = stored_player[player_nr].Frame;
7231
7232   int frame = getAnimationFrame(g->anim_frames,
7233                                 g->anim_delay,
7234                                 g->anim_mode,
7235                                 g->anim_start_frame,
7236                                 sync_frame);
7237
7238   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7239                       &g_em->src_x, &g_em->src_y, FALSE);
7240 }
7241
7242 void InitGraphicInfo_EM(void)
7243 {
7244   int i, j, p;
7245
7246 #if DEBUG_EM_GFX
7247   int num_em_gfx_errors = 0;
7248
7249   if (graphic_info_em_object[0][0].bitmap == NULL)
7250   {
7251     /* EM graphics not yet initialized in em_open_all() */
7252
7253     return;
7254   }
7255
7256   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7257 #endif
7258
7259   /* always start with reliable default values */
7260   for (i = 0; i < TILE_MAX; i++)
7261   {
7262     object_mapping[i].element_rnd = EL_UNKNOWN;
7263     object_mapping[i].is_backside = FALSE;
7264     object_mapping[i].action = ACTION_DEFAULT;
7265     object_mapping[i].direction = MV_NONE;
7266   }
7267
7268   /* always start with reliable default values */
7269   for (p = 0; p < MAX_PLAYERS; p++)
7270   {
7271     for (i = 0; i < SPR_MAX; i++)
7272     {
7273       player_mapping[p][i].element_rnd = EL_UNKNOWN;
7274       player_mapping[p][i].action = ACTION_DEFAULT;
7275       player_mapping[p][i].direction = MV_NONE;
7276     }
7277   }
7278
7279   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7280   {
7281     int e = em_object_mapping_list[i].element_em;
7282
7283     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7284     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7285
7286     if (em_object_mapping_list[i].action != -1)
7287       object_mapping[e].action = em_object_mapping_list[i].action;
7288
7289     if (em_object_mapping_list[i].direction != -1)
7290       object_mapping[e].direction =
7291         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7292   }
7293
7294   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7295   {
7296     int a = em_player_mapping_list[i].action_em;
7297     int p = em_player_mapping_list[i].player_nr;
7298
7299     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7300
7301     if (em_player_mapping_list[i].action != -1)
7302       player_mapping[p][a].action = em_player_mapping_list[i].action;
7303
7304     if (em_player_mapping_list[i].direction != -1)
7305       player_mapping[p][a].direction =
7306         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7307   }
7308
7309   for (i = 0; i < TILE_MAX; i++)
7310   {
7311     int element = object_mapping[i].element_rnd;
7312     int action = object_mapping[i].action;
7313     int direction = object_mapping[i].direction;
7314     boolean is_backside = object_mapping[i].is_backside;
7315     boolean action_exploding = ((action == ACTION_EXPLODING ||
7316                                  action == ACTION_SMASHED_BY_ROCK ||
7317                                  action == ACTION_SMASHED_BY_SPRING) &&
7318                                 element != EL_DIAMOND);
7319     boolean action_active = (action == ACTION_ACTIVE);
7320     boolean action_other = (action == ACTION_OTHER);
7321
7322     for (j = 0; j < 8; j++)
7323     {
7324       int effective_element = get_effective_element_EM(i, j);
7325       int effective_action = (j < 7 ? action :
7326                               i == Xdrip_stretch ? action :
7327                               i == Xdrip_stretchB ? action :
7328                               i == Ydrip_s1 ? action :
7329                               i == Ydrip_s1B ? action :
7330                               i == Xball_1B ? action :
7331                               i == Xball_2 ? action :
7332                               i == Xball_2B ? action :
7333                               i == Yball_eat ? action :
7334                               i == Ykey_1_eat ? action :
7335                               i == Ykey_2_eat ? action :
7336                               i == Ykey_3_eat ? action :
7337                               i == Ykey_4_eat ? action :
7338                               i == Ykey_5_eat ? action :
7339                               i == Ykey_6_eat ? action :
7340                               i == Ykey_7_eat ? action :
7341                               i == Ykey_8_eat ? action :
7342                               i == Ylenses_eat ? action :
7343                               i == Ymagnify_eat ? action :
7344                               i == Ygrass_eat ? action :
7345                               i == Ydirt_eat ? action :
7346                               i == Xsand_stonein_1 ? action :
7347                               i == Xsand_stonein_2 ? action :
7348                               i == Xsand_stonein_3 ? action :
7349                               i == Xsand_stonein_4 ? action :
7350                               i == Xsand_stoneout_1 ? action :
7351                               i == Xsand_stoneout_2 ? action :
7352                               i == Xboom_android ? ACTION_EXPLODING :
7353                               action_exploding ? ACTION_EXPLODING :
7354                               action_active ? action :
7355                               action_other ? action :
7356                               ACTION_DEFAULT);
7357       int graphic = (el_act_dir2img(effective_element, effective_action,
7358                                     direction));
7359       int crumbled = (el_act_dir2crm(effective_element, effective_action,
7360                                      direction));
7361       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7362       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7363       boolean has_action_graphics = (graphic != base_graphic);
7364       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7365       struct GraphicInfo *g = &graphic_info[graphic];
7366       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7367       Bitmap *src_bitmap;
7368       int src_x, src_y;
7369       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7370       boolean special_animation = (action != ACTION_DEFAULT &&
7371                                    g->anim_frames == 3 &&
7372                                    g->anim_delay == 2 &&
7373                                    g->anim_mode & ANIM_LINEAR);
7374       int sync_frame = (i == Xdrip_stretch ? 7 :
7375                         i == Xdrip_stretchB ? 7 :
7376                         i == Ydrip_s2 ? j + 8 :
7377                         i == Ydrip_s2B ? j + 8 :
7378                         i == Xacid_1 ? 0 :
7379                         i == Xacid_2 ? 10 :
7380                         i == Xacid_3 ? 20 :
7381                         i == Xacid_4 ? 30 :
7382                         i == Xacid_5 ? 40 :
7383                         i == Xacid_6 ? 50 :
7384                         i == Xacid_7 ? 60 :
7385                         i == Xacid_8 ? 70 :
7386                         i == Xfake_acid_1 ? 0 :
7387                         i == Xfake_acid_2 ? 10 :
7388                         i == Xfake_acid_3 ? 20 :
7389                         i == Xfake_acid_4 ? 30 :
7390                         i == Xfake_acid_5 ? 40 :
7391                         i == Xfake_acid_6 ? 50 :
7392                         i == Xfake_acid_7 ? 60 :
7393                         i == Xfake_acid_8 ? 70 :
7394                         i == Xball_2 ? 7 :
7395                         i == Xball_2B ? j + 8 :
7396                         i == Yball_eat ? j + 1 :
7397                         i == Ykey_1_eat ? j + 1 :
7398                         i == Ykey_2_eat ? j + 1 :
7399                         i == Ykey_3_eat ? j + 1 :
7400                         i == Ykey_4_eat ? j + 1 :
7401                         i == Ykey_5_eat ? j + 1 :
7402                         i == Ykey_6_eat ? j + 1 :
7403                         i == Ykey_7_eat ? j + 1 :
7404                         i == Ykey_8_eat ? j + 1 :
7405                         i == Ylenses_eat ? j + 1 :
7406                         i == Ymagnify_eat ? j + 1 :
7407                         i == Ygrass_eat ? j + 1 :
7408                         i == Ydirt_eat ? j + 1 :
7409                         i == Xamoeba_1 ? 0 :
7410                         i == Xamoeba_2 ? 1 :
7411                         i == Xamoeba_3 ? 2 :
7412                         i == Xamoeba_4 ? 3 :
7413                         i == Xamoeba_5 ? 0 :
7414                         i == Xamoeba_6 ? 1 :
7415                         i == Xamoeba_7 ? 2 :
7416                         i == Xamoeba_8 ? 3 :
7417                         i == Xexit_2 ? j + 8 :
7418                         i == Xexit_3 ? j + 16 :
7419                         i == Xdynamite_1 ? 0 :
7420                         i == Xdynamite_2 ? 8 :
7421                         i == Xdynamite_3 ? 16 :
7422                         i == Xdynamite_4 ? 24 :
7423                         i == Xsand_stonein_1 ? j + 1 :
7424                         i == Xsand_stonein_2 ? j + 9 :
7425                         i == Xsand_stonein_3 ? j + 17 :
7426                         i == Xsand_stonein_4 ? j + 25 :
7427                         i == Xsand_stoneout_1 && j == 0 ? 0 :
7428                         i == Xsand_stoneout_1 && j == 1 ? 0 :
7429                         i == Xsand_stoneout_1 && j == 2 ? 1 :
7430                         i == Xsand_stoneout_1 && j == 3 ? 2 :
7431                         i == Xsand_stoneout_1 && j == 4 ? 2 :
7432                         i == Xsand_stoneout_1 && j == 5 ? 3 :
7433                         i == Xsand_stoneout_1 && j == 6 ? 4 :
7434                         i == Xsand_stoneout_1 && j == 7 ? 4 :
7435                         i == Xsand_stoneout_2 && j == 0 ? 5 :
7436                         i == Xsand_stoneout_2 && j == 1 ? 6 :
7437                         i == Xsand_stoneout_2 && j == 2 ? 7 :
7438                         i == Xsand_stoneout_2 && j == 3 ? 8 :
7439                         i == Xsand_stoneout_2 && j == 4 ? 9 :
7440                         i == Xsand_stoneout_2 && j == 5 ? 11 :
7441                         i == Xsand_stoneout_2 && j == 6 ? 13 :
7442                         i == Xsand_stoneout_2 && j == 7 ? 15 :
7443                         i == Xboom_bug && j == 1 ? 2 :
7444                         i == Xboom_bug && j == 2 ? 2 :
7445                         i == Xboom_bug && j == 3 ? 4 :
7446                         i == Xboom_bug && j == 4 ? 4 :
7447                         i == Xboom_bug && j == 5 ? 2 :
7448                         i == Xboom_bug && j == 6 ? 2 :
7449                         i == Xboom_bug && j == 7 ? 0 :
7450                         i == Xboom_bomb && j == 1 ? 2 :
7451                         i == Xboom_bomb && j == 2 ? 2 :
7452                         i == Xboom_bomb && j == 3 ? 4 :
7453                         i == Xboom_bomb && j == 4 ? 4 :
7454                         i == Xboom_bomb && j == 5 ? 2 :
7455                         i == Xboom_bomb && j == 6 ? 2 :
7456                         i == Xboom_bomb && j == 7 ? 0 :
7457                         i == Xboom_android && j == 7 ? 6 :
7458                         i == Xboom_1 && j == 1 ? 2 :
7459                         i == Xboom_1 && j == 2 ? 2 :
7460                         i == Xboom_1 && j == 3 ? 4 :
7461                         i == Xboom_1 && j == 4 ? 4 :
7462                         i == Xboom_1 && j == 5 ? 6 :
7463                         i == Xboom_1 && j == 6 ? 6 :
7464                         i == Xboom_1 && j == 7 ? 8 :
7465                         i == Xboom_2 && j == 0 ? 8 :
7466                         i == Xboom_2 && j == 1 ? 8 :
7467                         i == Xboom_2 && j == 2 ? 10 :
7468                         i == Xboom_2 && j == 3 ? 10 :
7469                         i == Xboom_2 && j == 4 ? 10 :
7470                         i == Xboom_2 && j == 5 ? 12 :
7471                         i == Xboom_2 && j == 6 ? 12 :
7472                         i == Xboom_2 && j == 7 ? 12 :
7473                         special_animation && j == 4 ? 3 :
7474                         effective_action != action ? 0 :
7475                         j);
7476
7477 #if DEBUG_EM_GFX
7478       Bitmap *debug_bitmap = g_em->bitmap;
7479       int debug_src_x = g_em->src_x;
7480       int debug_src_y = g_em->src_y;
7481 #endif
7482
7483       int frame = getAnimationFrame(g->anim_frames,
7484                                     g->anim_delay,
7485                                     g->anim_mode,
7486                                     g->anim_start_frame,
7487                                     sync_frame);
7488
7489       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7490                           g->double_movement && is_backside);
7491
7492       g_em->bitmap = src_bitmap;
7493       g_em->src_x = src_x;
7494       g_em->src_y = src_y;
7495       g_em->src_offset_x = 0;
7496       g_em->src_offset_y = 0;
7497       g_em->dst_offset_x = 0;
7498       g_em->dst_offset_y = 0;
7499       g_em->width  = TILEX;
7500       g_em->height = TILEY;
7501
7502       g_em->preserve_background = FALSE;
7503
7504       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7505                                sync_frame);
7506
7507       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7508                                    effective_action == ACTION_MOVING  ||
7509                                    effective_action == ACTION_PUSHING ||
7510                                    effective_action == ACTION_EATING)) ||
7511           (!has_action_graphics && (effective_action == ACTION_FILLING ||
7512                                     effective_action == ACTION_EMPTYING)))
7513       {
7514         int move_dir =
7515           (effective_action == ACTION_FALLING ||
7516            effective_action == ACTION_FILLING ||
7517            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7518         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7519         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
7520         int num_steps = (i == Ydrip_s1  ? 16 :
7521                          i == Ydrip_s1B ? 16 :
7522                          i == Ydrip_s2  ? 16 :
7523                          i == Ydrip_s2B ? 16 :
7524                          i == Xsand_stonein_1 ? 32 :
7525                          i == Xsand_stonein_2 ? 32 :
7526                          i == Xsand_stonein_3 ? 32 :
7527                          i == Xsand_stonein_4 ? 32 :
7528                          i == Xsand_stoneout_1 ? 16 :
7529                          i == Xsand_stoneout_2 ? 16 : 8);
7530         int cx = ABS(dx) * (TILEX / num_steps);
7531         int cy = ABS(dy) * (TILEY / num_steps);
7532         int step_frame = (i == Ydrip_s2         ? j + 8 :
7533                           i == Ydrip_s2B        ? j + 8 :
7534                           i == Xsand_stonein_2  ? j + 8 :
7535                           i == Xsand_stonein_3  ? j + 16 :
7536                           i == Xsand_stonein_4  ? j + 24 :
7537                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7538         int step = (is_backside ? step_frame : num_steps - step_frame);
7539
7540         if (is_backside)        /* tile where movement starts */
7541         {
7542           if (dx < 0 || dy < 0)
7543           {
7544             g_em->src_offset_x = cx * step;
7545             g_em->src_offset_y = cy * step;
7546           }
7547           else
7548           {
7549             g_em->dst_offset_x = cx * step;
7550             g_em->dst_offset_y = cy * step;
7551           }
7552         }
7553         else                    /* tile where movement ends */
7554         {
7555           if (dx < 0 || dy < 0)
7556           {
7557             g_em->dst_offset_x = cx * step;
7558             g_em->dst_offset_y = cy * step;
7559           }
7560           else
7561           {
7562             g_em->src_offset_x = cx * step;
7563             g_em->src_offset_y = cy * step;
7564           }
7565         }
7566
7567         g_em->width  = TILEX - cx * step;
7568         g_em->height = TILEY - cy * step;
7569       }
7570
7571       /* create unique graphic identifier to decide if tile must be redrawn */
7572       /* bit 31 - 16 (16 bit): EM style graphic
7573          bit 15 - 12 ( 4 bit): EM style frame
7574          bit 11 -  6 ( 6 bit): graphic width
7575          bit  5 -  0 ( 6 bit): graphic height */
7576       g_em->unique_identifier =
7577         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7578
7579 #if DEBUG_EM_GFX
7580
7581       /* skip check for EMC elements not contained in original EMC artwork */
7582       if (element == EL_EMC_FAKE_ACID)
7583         continue;
7584
7585       if (g_em->bitmap != debug_bitmap ||
7586           g_em->src_x != debug_src_x ||
7587           g_em->src_y != debug_src_y ||
7588           g_em->src_offset_x != 0 ||
7589           g_em->src_offset_y != 0 ||
7590           g_em->dst_offset_x != 0 ||
7591           g_em->dst_offset_y != 0 ||
7592           g_em->width != TILEX ||
7593           g_em->height != TILEY)
7594       {
7595         static int last_i = -1;
7596
7597         if (i != last_i)
7598         {
7599           printf("\n");
7600           last_i = i;
7601         }
7602
7603         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7604                i, element, element_info[element].token_name,
7605                element_action_info[effective_action].suffix, direction);
7606
7607         if (element != effective_element)
7608           printf(" [%d ('%s')]",
7609                  effective_element,
7610                  element_info[effective_element].token_name);
7611
7612         printf("\n");
7613
7614         if (g_em->bitmap != debug_bitmap)
7615           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7616                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7617
7618         if (g_em->src_x != debug_src_x ||
7619             g_em->src_y != debug_src_y)
7620           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7621                  j, (is_backside ? 'B' : 'F'),
7622                  g_em->src_x, g_em->src_y,
7623                  g_em->src_x / 32, g_em->src_y / 32,
7624                  debug_src_x, debug_src_y,
7625                  debug_src_x / 32, debug_src_y / 32);
7626
7627         if (g_em->src_offset_x != 0 ||
7628             g_em->src_offset_y != 0 ||
7629             g_em->dst_offset_x != 0 ||
7630             g_em->dst_offset_y != 0)
7631           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7632                  j, is_backside,
7633                  g_em->src_offset_x, g_em->src_offset_y,
7634                  g_em->dst_offset_x, g_em->dst_offset_y);
7635
7636         if (g_em->width != TILEX ||
7637             g_em->height != TILEY)
7638           printf("    %d (%d): size %d,%d should be %d,%d\n",
7639                  j, is_backside,
7640                  g_em->width, g_em->height, TILEX, TILEY);
7641
7642         num_em_gfx_errors++;
7643       }
7644 #endif
7645
7646     }
7647   }
7648
7649   for (i = 0; i < TILE_MAX; i++)
7650   {
7651     for (j = 0; j < 8; j++)
7652     {
7653       int element = object_mapping[i].element_rnd;
7654       int action = object_mapping[i].action;
7655       int direction = object_mapping[i].direction;
7656       boolean is_backside = object_mapping[i].is_backside;
7657       int graphic_action  = el_act_dir2img(element, action, direction);
7658       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7659
7660       if ((action == ACTION_SMASHED_BY_ROCK ||
7661            action == ACTION_SMASHED_BY_SPRING ||
7662            action == ACTION_EATING) &&
7663           graphic_action == graphic_default)
7664       {
7665         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
7666                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7667                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
7668                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7669                  Xspring);
7670
7671         /* no separate animation for "smashed by rock" -- use rock instead */
7672         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7673         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7674
7675         g_em->bitmap            = g_xx->bitmap;
7676         g_em->src_x             = g_xx->src_x;
7677         g_em->src_y             = g_xx->src_y;
7678         g_em->src_offset_x      = g_xx->src_offset_x;
7679         g_em->src_offset_y      = g_xx->src_offset_y;
7680         g_em->dst_offset_x      = g_xx->dst_offset_x;
7681         g_em->dst_offset_y      = g_xx->dst_offset_y;
7682         g_em->width             = g_xx->width;
7683         g_em->height            = g_xx->height;
7684         g_em->unique_identifier = g_xx->unique_identifier;
7685
7686         if (!is_backside)
7687           g_em->preserve_background = TRUE;
7688       }
7689     }
7690   }
7691
7692   for (p = 0; p < MAX_PLAYERS; p++)
7693   {
7694     for (i = 0; i < SPR_MAX; i++)
7695     {
7696       int element = player_mapping[p][i].element_rnd;
7697       int action = player_mapping[p][i].action;
7698       int direction = player_mapping[p][i].direction;
7699
7700       for (j = 0; j < 8; j++)
7701       {
7702         int effective_element = element;
7703         int effective_action = action;
7704         int graphic = (direction == MV_NONE ?
7705                        el_act2img(effective_element, effective_action) :
7706                        el_act_dir2img(effective_element, effective_action,
7707                                       direction));
7708         struct GraphicInfo *g = &graphic_info[graphic];
7709         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7710         Bitmap *src_bitmap;
7711         int src_x, src_y;
7712         int sync_frame = j;
7713
7714 #if DEBUG_EM_GFX
7715         Bitmap *debug_bitmap = g_em->bitmap;
7716         int debug_src_x = g_em->src_x;
7717         int debug_src_y = g_em->src_y;
7718 #endif
7719
7720         int frame = getAnimationFrame(g->anim_frames,
7721                                       g->anim_delay,
7722                                       g->anim_mode,
7723                                       g->anim_start_frame,
7724                                       sync_frame);
7725
7726         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7727
7728         g_em->bitmap = src_bitmap;
7729         g_em->src_x = src_x;
7730         g_em->src_y = src_y;
7731         g_em->src_offset_x = 0;
7732         g_em->src_offset_y = 0;
7733         g_em->dst_offset_x = 0;
7734         g_em->dst_offset_y = 0;
7735         g_em->width  = TILEX;
7736         g_em->height = TILEY;
7737
7738 #if DEBUG_EM_GFX
7739
7740         /* skip check for EMC elements not contained in original EMC artwork */
7741         if (element == EL_PLAYER_3 ||
7742             element == EL_PLAYER_4)
7743           continue;
7744
7745         if (g_em->bitmap != debug_bitmap ||
7746             g_em->src_x != debug_src_x ||
7747             g_em->src_y != debug_src_y)
7748         {
7749           static int last_i = -1;
7750
7751           if (i != last_i)
7752           {
7753             printf("\n");
7754             last_i = i;
7755           }
7756
7757           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7758                  p, i, element, element_info[element].token_name,
7759                  element_action_info[effective_action].suffix, direction);
7760
7761           if (element != effective_element)
7762             printf(" [%d ('%s')]",
7763                    effective_element,
7764                    element_info[effective_element].token_name);
7765
7766           printf("\n");
7767
7768           if (g_em->bitmap != debug_bitmap)
7769             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
7770                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
7771
7772           if (g_em->src_x != debug_src_x ||
7773               g_em->src_y != debug_src_y)
7774             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7775                    j,
7776                    g_em->src_x, g_em->src_y,
7777                    g_em->src_x / 32, g_em->src_y / 32,
7778                    debug_src_x, debug_src_y,
7779                    debug_src_x / 32, debug_src_y / 32);
7780
7781           num_em_gfx_errors++;
7782         }
7783 #endif
7784
7785       }
7786     }
7787   }
7788
7789 #if DEBUG_EM_GFX
7790   printf("\n");
7791   printf("::: [%d errors found]\n", num_em_gfx_errors);
7792
7793   exit(0);
7794 #endif
7795 }
7796
7797 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7798                                 boolean any_player_moving,
7799                                 boolean any_player_snapping,
7800                                 boolean any_player_dropping)
7801 {
7802   static boolean player_was_waiting = TRUE;
7803
7804   if (frame == 0 && !any_player_dropping)
7805   {
7806     if (!player_was_waiting)
7807     {
7808       if (!SaveEngineSnapshotToList())
7809         return;
7810
7811       player_was_waiting = TRUE;
7812     }
7813   }
7814   else if (any_player_moving || any_player_snapping || any_player_dropping)
7815   {
7816     player_was_waiting = FALSE;
7817   }
7818 }
7819
7820 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7821                                 boolean murphy_is_dropping)
7822 {
7823   static boolean player_was_waiting = TRUE;
7824
7825   if (murphy_is_waiting)
7826   {
7827     if (!player_was_waiting)
7828     {
7829       if (!SaveEngineSnapshotToList())
7830         return;
7831
7832       player_was_waiting = TRUE;
7833     }
7834   }
7835   else
7836   {
7837     player_was_waiting = FALSE;
7838   }
7839 }
7840
7841 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7842                             boolean any_player_moving,
7843                             boolean any_player_snapping,
7844                             boolean any_player_dropping)
7845 {
7846   if (tape.single_step && tape.recording && !tape.pausing)
7847     if (frame == 0 && !any_player_dropping)
7848       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7849
7850   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7851                              any_player_snapping, any_player_dropping);
7852 }
7853
7854 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7855                             boolean murphy_is_dropping)
7856 {
7857   if (tape.single_step && tape.recording && !tape.pausing)
7858     if (murphy_is_waiting)
7859       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7860
7861   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7862 }
7863
7864 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7865                          int graphic, int sync_frame, int x, int y)
7866 {
7867   int frame = getGraphicAnimationFrame(graphic, sync_frame);
7868
7869   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7870 }
7871
7872 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7873 {
7874   return (IS_NEXT_FRAME(sync_frame, graphic));
7875 }
7876
7877 int getGraphicInfo_Delay(int graphic)
7878 {
7879   return graphic_info[graphic].anim_delay;
7880 }
7881
7882 void PlayMenuSoundExt(int sound)
7883 {
7884   if (sound == SND_UNDEFINED)
7885     return;
7886
7887   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7888       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7889     return;
7890
7891   if (IS_LOOP_SOUND(sound))
7892     PlaySoundLoop(sound);
7893   else
7894     PlaySound(sound);
7895 }
7896
7897 void PlayMenuSound()
7898 {
7899   PlayMenuSoundExt(menu.sound[game_status]);
7900 }
7901
7902 void PlayMenuSoundStereo(int sound, int stereo_position)
7903 {
7904   if (sound == SND_UNDEFINED)
7905     return;
7906
7907   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7908       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7909     return;
7910
7911   if (IS_LOOP_SOUND(sound))
7912     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7913   else
7914     PlaySoundStereo(sound, stereo_position);
7915 }
7916
7917 void PlayMenuSoundIfLoopExt(int sound)
7918 {
7919   if (sound == SND_UNDEFINED)
7920     return;
7921
7922   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7923       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7924     return;
7925
7926   if (IS_LOOP_SOUND(sound))
7927     PlaySoundLoop(sound);
7928 }
7929
7930 void PlayMenuSoundIfLoop()
7931 {
7932   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7933 }
7934
7935 void PlayMenuMusicExt(int music)
7936 {
7937   if (music == MUS_UNDEFINED)
7938     return;
7939
7940   if (!setup.sound_music)
7941     return;
7942
7943   PlayMusic(music);
7944 }
7945
7946 void PlayMenuMusic()
7947 {
7948   PlayMenuMusicExt(menu.music[game_status]);
7949 }
7950
7951 void PlaySoundActivating()
7952 {
7953 #if 0
7954   PlaySound(SND_MENU_ITEM_ACTIVATING);
7955 #endif
7956 }
7957
7958 void PlaySoundSelecting()
7959 {
7960 #if 0
7961   PlaySound(SND_MENU_ITEM_SELECTING);
7962 #endif
7963 }
7964
7965 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7966 {
7967   boolean change_fullscreen = (setup.fullscreen !=
7968                                video.fullscreen_enabled);
7969   boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7970                                     !strEqual(setup.fullscreen_mode,
7971                                               video.fullscreen_mode_current));
7972   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7973                                            setup.window_scaling_percent !=
7974                                            video.window_scaling_percent);
7975
7976   if (change_window_scaling_percent && video.fullscreen_enabled)
7977     return;
7978
7979   if (!change_window_scaling_percent && !video.fullscreen_available)
7980     return;
7981
7982 #if defined(TARGET_SDL2)
7983   if (change_window_scaling_percent)
7984   {
7985     SDLSetWindowScaling(setup.window_scaling_percent);
7986
7987     return;
7988   }
7989   else if (change_fullscreen)
7990   {
7991     SDLSetWindowFullscreen(setup.fullscreen);
7992
7993     /* set setup value according to successfully changed fullscreen mode */
7994     setup.fullscreen = video.fullscreen_enabled;
7995
7996     return;
7997   }
7998 #endif
7999
8000   if (change_fullscreen ||
8001       change_fullscreen_mode ||
8002       change_window_scaling_percent)
8003   {
8004     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8005
8006     /* save backbuffer content which gets lost when toggling fullscreen mode */
8007     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8008
8009     if (change_fullscreen_mode)
8010     {
8011       /* keep fullscreen, but change fullscreen mode (screen resolution) */
8012       video.fullscreen_enabled = FALSE;         /* force new fullscreen mode */
8013     }
8014
8015     if (change_window_scaling_percent)
8016     {
8017       /* keep window mode, but change window scaling */
8018       video.fullscreen_enabled = TRUE;          /* force new window scaling */
8019     }
8020
8021     /* toggle fullscreen */
8022     ChangeVideoModeIfNeeded(setup.fullscreen);
8023
8024     /* set setup value according to successfully changed fullscreen mode */
8025     setup.fullscreen = video.fullscreen_enabled;
8026
8027     /* restore backbuffer content from temporary backbuffer backup bitmap */
8028     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8029
8030     FreeBitmap(tmp_backbuffer);
8031
8032     /* update visible window/screen */
8033     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8034   }
8035 }
8036
8037 void ChangeViewportPropertiesIfNeeded()
8038 {
8039   int gfx_game_mode = game_status;
8040   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8041                         game_status);
8042   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8043   struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8044   struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8045   struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8046   int border_size       = vp_playfield->border_size;
8047   int new_sx            = vp_playfield->x + border_size;
8048   int new_sy            = vp_playfield->y + border_size;
8049   int new_sxsize        = vp_playfield->width  - 2 * border_size;
8050   int new_sysize        = vp_playfield->height - 2 * border_size;
8051   int new_real_sx       = vp_playfield->x;
8052   int new_real_sy       = vp_playfield->y;
8053   int new_full_sxsize   = vp_playfield->width;
8054   int new_full_sysize   = vp_playfield->height;
8055   int new_dx            = vp_door_1->x;
8056   int new_dy            = vp_door_1->y;
8057   int new_dxsize        = vp_door_1->width;
8058   int new_dysize        = vp_door_1->height;
8059   int new_vx            = vp_door_2->x;
8060   int new_vy            = vp_door_2->y;
8061   int new_vxsize        = vp_door_2->width;
8062   int new_vysize        = vp_door_2->height;
8063   int new_ex            = vp_door_3->x;
8064   int new_ey            = vp_door_3->y;
8065   int new_exsize        = vp_door_3->width;
8066   int new_eysize        = vp_door_3->height;
8067   int new_tilesize_var =
8068     (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8069
8070   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8071                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8072   int new_scr_fieldx = new_sxsize / tilesize;
8073   int new_scr_fieldy = new_sysize / tilesize;
8074   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8075   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8076   boolean init_gfx_buffers = FALSE;
8077   boolean init_video_buffer = FALSE;
8078   boolean init_gadgets_and_toons = FALSE;
8079   boolean init_em_graphics = FALSE;
8080   boolean drawing_area_changed = FALSE;
8081
8082   if (viewport.window.width  != WIN_XSIZE ||
8083       viewport.window.height != WIN_YSIZE)
8084   {
8085     WIN_XSIZE = viewport.window.width;
8086     WIN_YSIZE = viewport.window.height;
8087
8088     init_video_buffer = TRUE;
8089     init_gfx_buffers = TRUE;
8090
8091     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8092   }
8093
8094   if (new_scr_fieldx != SCR_FIELDX ||
8095       new_scr_fieldy != SCR_FIELDY)
8096   {
8097     /* this always toggles between MAIN and GAME when using small tile size */
8098
8099     SCR_FIELDX = new_scr_fieldx;
8100     SCR_FIELDY = new_scr_fieldy;
8101
8102     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8103   }
8104
8105   if (new_sx != SX ||
8106       new_sy != SY ||
8107       new_dx != DX ||
8108       new_dy != DY ||
8109       new_vx != VX ||
8110       new_vy != VY ||
8111       new_ex != EX ||
8112       new_ey != EY ||
8113       new_sxsize != SXSIZE ||
8114       new_sysize != SYSIZE ||
8115       new_dxsize != DXSIZE ||
8116       new_dysize != DYSIZE ||
8117       new_vxsize != VXSIZE ||
8118       new_vysize != VYSIZE ||
8119       new_exsize != EXSIZE ||
8120       new_eysize != EYSIZE ||
8121       new_real_sx != REAL_SX ||
8122       new_real_sy != REAL_SY ||
8123       new_full_sxsize != FULL_SXSIZE ||
8124       new_full_sysize != FULL_SYSIZE ||
8125       new_tilesize_var != TILESIZE_VAR
8126       )
8127   {
8128     if (new_tilesize_var != TILESIZE_VAR)
8129     {
8130       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8131
8132       // changing tile size invalidates scroll values of engine snapshots
8133       FreeEngineSnapshotSingle();
8134
8135       // changing tile size requires update of graphic mapping for EM engine
8136       init_em_graphics = TRUE;
8137     }
8138
8139     if (new_sx != SX ||
8140         new_sy != SY ||
8141         new_sxsize != SXSIZE ||
8142         new_sysize != SYSIZE ||
8143         new_real_sx != REAL_SX ||
8144         new_real_sy != REAL_SY ||
8145         new_full_sxsize != FULL_SXSIZE ||
8146         new_full_sysize != FULL_SYSIZE)
8147     {
8148       if (!init_video_buffer)
8149         drawing_area_changed = TRUE;
8150     }
8151
8152     SX = new_sx;
8153     SY = new_sy;
8154     DX = new_dx;
8155     DY = new_dy;
8156     VX = new_vx;
8157     VY = new_vy;
8158     EX = new_ex;
8159     EY = new_ey;
8160     SXSIZE = new_sxsize;
8161     SYSIZE = new_sysize;
8162     DXSIZE = new_dxsize;
8163     DYSIZE = new_dysize;
8164     VXSIZE = new_vxsize;
8165     VYSIZE = new_vysize;
8166     EXSIZE = new_exsize;
8167     EYSIZE = new_eysize;
8168     REAL_SX = new_real_sx;
8169     REAL_SY = new_real_sy;
8170     FULL_SXSIZE = new_full_sxsize;
8171     FULL_SYSIZE = new_full_sysize;
8172     TILESIZE_VAR = new_tilesize_var;
8173
8174     init_gfx_buffers = TRUE;
8175     init_gadgets_and_toons = TRUE;
8176
8177     // printf("::: viewports: init_gfx_buffers\n");
8178     // printf("::: viewports: init_gadgets_and_toons\n");
8179   }
8180
8181   if (init_gfx_buffers)
8182   {
8183     // printf("::: init_gfx_buffers\n");
8184
8185     SCR_FIELDX = new_scr_fieldx_buffers;
8186     SCR_FIELDY = new_scr_fieldy_buffers;
8187
8188     InitGfxBuffers();
8189
8190     SCR_FIELDX = new_scr_fieldx;
8191     SCR_FIELDY = new_scr_fieldy;
8192
8193     gfx.drawing_area_changed = drawing_area_changed;
8194
8195     SetDrawDeactivationMask(REDRAW_NONE);
8196     SetDrawBackgroundMask(REDRAW_FIELD);
8197   }
8198
8199   if (init_video_buffer)
8200   {
8201     // printf("::: init_video_buffer\n");
8202
8203     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8204   }
8205
8206   if (init_gadgets_and_toons)
8207   {
8208     // printf("::: init_gadgets_and_toons\n");
8209
8210     InitGadgets();
8211     InitToons();
8212   }
8213
8214   if (init_em_graphics)
8215   {
8216       InitGraphicInfo_EM();
8217   }
8218 }