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