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