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