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