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