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