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