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