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