rnd-20070123-1-src
[rocksndiamonds.git] / src / tools.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * tools.c                                                  *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "game.h"
18 #include "events.h"
19 #include "cartoons.h"
20 #include "network.h"
21 #include "tape.h"
22 #include "screens.h"
23
24
25 /* select level set with EMC X11 graphics before activating EM GFX debugging */
26 #define DEBUG_EM_GFX    0
27
28 /* tool button identifiers */
29 #define TOOL_CTRL_ID_YES        0
30 #define TOOL_CTRL_ID_NO         1
31 #define TOOL_CTRL_ID_CONFIRM    2
32 #define TOOL_CTRL_ID_PLAYER_1   3
33 #define TOOL_CTRL_ID_PLAYER_2   4
34 #define TOOL_CTRL_ID_PLAYER_3   5
35 #define TOOL_CTRL_ID_PLAYER_4   6
36
37 #define NUM_TOOL_BUTTONS        7
38
39 /* forward declaration for internal use */
40 static void UnmapToolButtons();
41 static void HandleToolButtons(struct GadgetInfo *);
42 static int el_act_dir2crm(int, int, int);
43 static int el_act2crm(int, int);
44
45 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
46 static int request_gadget_id = -1;
47
48 static char *print_if_not_empty(int element)
49 {
50   static char *s = NULL;
51   char *token_name = element_info[element].token_name;
52
53   if (s != NULL)
54     free(s);
55
56   s = checked_malloc(strlen(token_name) + 10 + 1);
57
58   if (element != EL_EMPTY)
59     sprintf(s, "%d\t['%s']", element, token_name);
60   else
61     sprintf(s, "%d", element);
62
63   return s;
64 }
65
66 void DumpTile(int x, int y)
67 {
68   int sx = SCREENX(x);
69   int sy = SCREENY(y);
70
71   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
72   {
73     x--;
74     y--;
75   }
76
77   printf_line("-", 79);
78   printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
79   printf_line("-", 79);
80
81   if (!IN_LEV_FIELD(x, y))
82   {
83     printf("(not in level field)\n");
84     printf("\n");
85
86     return;
87   }
88
89   printf("  Feld:        %d\t['%s']\n", Feld[x][y],
90          element_info[Feld[x][y]].token_name);
91   printf("  Back:        %s\n", print_if_not_empty(Back[x][y]));
92   printf("  Store:       %s\n", print_if_not_empty(Store[x][y]));
93   printf("  Store2:      %s\n", print_if_not_empty(Store2[x][y]));
94   printf("  StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
95   printf("  MovPos:      %d\n", MovPos[x][y]);
96   printf("  MovDir:      %d\n", MovDir[x][y]);
97   printf("  MovDelay:    %d\n", MovDelay[x][y]);
98   printf("  ChangeDelay: %d\n", ChangeDelay[x][y]);
99   printf("  CustomValue: %d\n", CustomValue[x][y]);
100   printf("  GfxElement:  %d\n", GfxElement[x][y]);
101   printf("  GfxAction:   %d\n", GfxAction[x][y]);
102   printf("  GfxFrame:    %d\n", GfxFrame[x][y]);
103   printf("\n");
104 }
105
106 void SetDrawtoField(int mode)
107 {
108   if (mode == DRAW_BUFFERED && setup.soft_scrolling)
109   {
110     FX = TILEX;
111     FY = TILEY;
112     BX1 = -1;
113     BY1 = -1;
114     BX2 = SCR_FIELDX;
115     BY2 = SCR_FIELDY;
116     redraw_x1 = 1;
117     redraw_y1 = 1;
118
119     drawto_field = fieldbuffer;
120   }
121   else  /* DRAW_DIRECT, DRAW_BACKBUFFER */
122   {
123     FX = SX;
124     FY = SY;
125     BX1 = 0;
126     BY1 = 0;
127     BX2 = SCR_FIELDX - 1;
128     BY2 = SCR_FIELDY - 1;
129     redraw_x1 = 0;
130     redraw_y1 = 0;
131
132     drawto_field = (mode == DRAW_DIRECT ? window :  backbuffer);
133   }
134 }
135
136 void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
137 {
138   if (game_status == GAME_MODE_PLAYING &&
139       level.game_engine_type == GAME_ENGINE_TYPE_EM)
140   {
141     /* currently there is no partial redraw -- always redraw whole playfield */
142     RedrawPlayfield_EM(TRUE);
143
144     /* blit playfield from scroll buffer to normal back buffer for fading in */
145     BlitScreenToBitmap_EM(backbuffer);
146   }
147   else if (game_status == GAME_MODE_PLAYING && !game.envelope_active)
148   {
149     if (force_redraw)
150     {
151       x = gfx.sx - TILEX;
152       y = gfx.sy - TILEY;
153       width = gfx.sxsize + 2 * TILEX;
154       height = gfx.sysize + 2 * TILEY;
155     }
156
157     if (force_redraw || setup.direct_draw)
158     {
159       int xx, yy;
160       int x1 = (x - SX) / TILEX, y1 = (y - SY) / TILEY;
161       int x2 = (x - SX + width) / TILEX, y2 = (y - SY + height) / TILEY;
162
163       if (setup.direct_draw)
164         SetDrawtoField(DRAW_BACKBUFFER);
165
166       for (xx = BX1; xx <= BX2; xx++)
167         for (yy = BY1; yy <= BY2; yy++)
168           if (xx >= x1 && xx <= x2 && yy >= y1 && yy <= y2)
169             DrawScreenField(xx, yy);
170       DrawAllPlayers();
171
172       if (setup.direct_draw)
173         SetDrawtoField(DRAW_DIRECT);
174     }
175
176     if (setup.soft_scrolling)
177     {
178       int fx = FX, fy = FY;
179
180       fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
181       fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
182
183       BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
184     }
185   }
186
187   if (force_redraw)
188   {
189     x = gfx.sx;
190     y = gfx.sy;
191     width = gfx.sxsize;
192     height = gfx.sysize;
193   }
194
195   BlitBitmap(drawto, window, x, y, width, height, x, y);
196 }
197
198 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
199 {
200   Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
201
202   SetClipOrigin(bitmap, bitmap->stored_clip_gc, 0, 0);
203   BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
204 }
205
206 void DrawMaskedBorder_FIELD()
207 {
208   if (game_status >= GAME_MODE_TITLE &&
209       game_status <= GAME_MODE_PLAYING &&
210       border.draw_masked[game_status])
211     DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
212 }
213
214 void DrawMaskedBorder_DOOR_1()
215 {
216   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
217       (game_status != GAME_MODE_EDITOR ||
218        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
219     DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
220 }
221
222 void DrawMaskedBorder_DOOR_2()
223 {
224   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
225       game_status != GAME_MODE_EDITOR)
226     DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
227 }
228
229 void DrawMaskedBorder_DOOR_3()
230 {
231   /* currently not available */
232 }
233
234 void DrawMaskedBorder_ALL()
235 {
236   DrawMaskedBorder_FIELD();
237   DrawMaskedBorder_DOOR_1();
238   DrawMaskedBorder_DOOR_2();
239   DrawMaskedBorder_DOOR_3();
240 }
241
242 void DrawMaskedBorder(int redraw_mask)
243 {
244   /* do not draw masked screen borders when displaying title screens */
245   if (effectiveGameStatus() == GAME_MODE_TITLE ||
246       effectiveGameStatus() == GAME_MODE_MESSAGE)
247     return;
248
249   if (redraw_mask & REDRAW_ALL)
250     DrawMaskedBorder_ALL();
251   else
252   {
253     if (redraw_mask & REDRAW_FIELD)
254       DrawMaskedBorder_FIELD();
255     if (redraw_mask & REDRAW_DOOR_1)
256       DrawMaskedBorder_DOOR_1();
257     if (redraw_mask & REDRAW_DOOR_2)
258       DrawMaskedBorder_DOOR_2();
259     if (redraw_mask & REDRAW_DOOR_3)
260       DrawMaskedBorder_DOOR_3();
261   }
262 }
263
264 void BackToFront()
265 {
266   int x,y;
267   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
268
269   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
270     redraw_mask &= ~REDRAW_MAIN;
271
272   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
273     redraw_mask |= REDRAW_FIELD;
274
275   if (redraw_mask & REDRAW_FIELD)
276     redraw_mask &= ~REDRAW_TILES;
277
278   if (redraw_mask == REDRAW_NONE)
279     return;
280
281   if (redraw_mask & REDRAW_TILES &&
282       game_status == GAME_MODE_PLAYING &&
283       border.draw_masked[GAME_MODE_PLAYING])
284     redraw_mask |= REDRAW_FIELD;
285
286   if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
287   {
288     static boolean last_frame_skipped = FALSE;
289     boolean skip_even_when_not_scrolling = TRUE;
290     boolean just_scrolling = (ScreenMovDir != 0);
291     boolean verbose = FALSE;
292
293     if (global.fps_slowdown_factor > 1 &&
294         (FrameCounter % global.fps_slowdown_factor) &&
295         (just_scrolling || skip_even_when_not_scrolling))
296     {
297       redraw_mask &= ~REDRAW_MAIN;
298
299       last_frame_skipped = TRUE;
300
301       if (verbose)
302         printf("FRAME SKIPPED\n");
303     }
304     else
305     {
306       if (last_frame_skipped)
307         redraw_mask |= REDRAW_FIELD;
308
309       last_frame_skipped = FALSE;
310
311       if (verbose)
312         printf("frame not skipped\n");
313     }
314   }
315
316   /* synchronize X11 graphics at this point; if we would synchronize the
317      display immediately after the buffer switching (after the XFlush),
318      this could mean that we have to wait for the graphics to complete,
319      although we could go on doing calculations for the next frame */
320
321   SyncDisplay();
322
323   /* prevent drawing masked border to backbuffer when using playfield buffer */
324   if (game_status != GAME_MODE_PLAYING ||
325       redraw_mask & REDRAW_FROM_BACKBUFFER ||
326       buffer == backbuffer)
327     DrawMaskedBorder(redraw_mask);
328   else
329     DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
330
331   if (redraw_mask & REDRAW_ALL)
332   {
333     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
334
335     redraw_mask = REDRAW_NONE;
336   }
337
338   if (redraw_mask & REDRAW_FIELD)
339   {
340     if (game_status != GAME_MODE_PLAYING ||
341         redraw_mask & REDRAW_FROM_BACKBUFFER)
342     {
343       BlitBitmap(backbuffer, window,
344                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
345     }
346     else
347     {
348       int fx = FX, fy = FY;
349
350       if (setup.soft_scrolling)
351       {
352         fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
353         fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
354       }
355
356       if (setup.soft_scrolling ||
357           ABS(ScreenMovPos) + ScrollStepSize == TILEX ||
358           ABS(ScreenMovPos) == ScrollStepSize ||
359           redraw_tiles > REDRAWTILES_THRESHOLD)
360       {
361         if (border.draw_masked[GAME_MODE_PLAYING])
362         {
363           if (buffer != backbuffer)
364           {
365             /* copy playfield buffer to backbuffer to add masked border */
366             BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
367             DrawMaskedBorder(REDRAW_FIELD);
368           }
369
370           BlitBitmap(backbuffer, window,
371                      REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
372                      REAL_SX, REAL_SY);
373         }
374         else
375         {
376           BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
377         }
378
379 #if 0
380 #ifdef DEBUG
381         printf("redrawing all (ScreenGfxPos == %d) because %s\n",
382                ScreenGfxPos,
383                (setup.soft_scrolling ?
384                 "setup.soft_scrolling" :
385                 ABS(ScreenGfxPos) + ScrollStepSize == TILEX ?
386                 "ABS(ScreenGfxPos) + ScrollStepSize == TILEX" :
387                 ABS(ScreenGfxPos) == ScrollStepSize ?
388                 "ABS(ScreenGfxPos) == ScrollStepSize" :
389                 "redraw_tiles > REDRAWTILES_THRESHOLD"));
390 #endif
391 #endif
392       }
393     }
394
395     redraw_mask &= ~REDRAW_MAIN;
396   }
397
398   if (redraw_mask & REDRAW_DOORS)
399   {
400     if (redraw_mask & REDRAW_DOOR_1)
401       BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
402
403     if (redraw_mask & REDRAW_DOOR_2)
404       BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
405
406     if (redraw_mask & REDRAW_DOOR_3)
407       BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
408
409     redraw_mask &= ~REDRAW_DOORS;
410   }
411
412   if (redraw_mask & REDRAW_MICROLEVEL)
413   {
414     BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
415                SX, SY + 10 * TILEY);
416
417     redraw_mask &= ~REDRAW_MICROLEVEL;
418   }
419
420   if (redraw_mask & REDRAW_TILES)
421   {
422     for (x = 0; x < SCR_FIELDX; x++)
423       for (y = 0 ; y < SCR_FIELDY; y++)
424         if (redraw[redraw_x1 + x][redraw_y1 + y])
425           BlitBitmap(buffer, window,
426                      FX + x * TILEX, FY + y * TILEY, TILEX, TILEY,
427                      SX + x * TILEX, SY + y * TILEY);
428   }
429
430   if (redraw_mask & REDRAW_FPS)         /* display frames per second */
431   {
432     char text[100];
433     char info1[100];
434
435     sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
436     if (!global.fps_slowdown)
437       info1[0] = '\0';
438
439     sprintf(text, "%.1f fps%s", global.frames_per_second, info1);
440     DrawTextExt(window, SX, SY, text, FONT_TEXT_2, BLIT_OPAQUE);
441   }
442
443   FlushDisplay();
444
445   for (x = 0; x < MAX_BUF_XSIZE; x++)
446     for (y = 0; y < MAX_BUF_YSIZE; y++)
447       redraw[x][y] = 0;
448   redraw_tiles = 0;
449   redraw_mask = REDRAW_NONE;
450 }
451
452 void FadeToFront()
453 {
454 #if 0
455   long fading_delay = 300;
456
457   if (setup.fading && (redraw_mask & REDRAW_FIELD))
458   {
459 #endif
460
461 #if 0
462     int x,y;
463
464     ClearRectangle(window, REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
465     FlushDisplay();
466
467     for (i = 0; i < 2 * FULL_SYSIZE; i++)
468     {
469       for (y = 0; y < FULL_SYSIZE; y++)
470       {
471         BlitBitmap(backbuffer, window,
472                    REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
473       }
474       FlushDisplay();
475       Delay(10);
476     }
477 #endif
478
479 #if 0
480     for (i = 1; i < FULL_SYSIZE; i+=2)
481       BlitBitmap(backbuffer, window,
482                  REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
483     FlushDisplay();
484     Delay(fading_delay);
485 #endif
486
487 #if 0
488     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, 0);
489     BlitBitmapMasked(backbuffer, window,
490                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
491                      REAL_SX,REAL_SY);
492     FlushDisplay();
493     Delay(fading_delay);
494
495     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, -1);
496     BlitBitmapMasked(backbuffer, window,
497                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
498                      REAL_SX,REAL_SY);
499     FlushDisplay();
500     Delay(fading_delay);
501
502     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, -1);
503     BlitBitmapMasked(backbuffer, window,
504                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
505                      REAL_SX,REAL_SY);
506     FlushDisplay();
507     Delay(fading_delay);
508
509     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, 0);
510     BlitBitmapMasked(backbuffer, window,
511                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
512                      REAL_SX,REAL_SY);
513     FlushDisplay();
514     Delay(fading_delay);
515
516     redraw_mask &= ~REDRAW_MAIN;
517   }
518 #endif
519
520   BackToFront();
521 }
522
523 void FadeExt(int fade_mask, int fade_mode)
524 {
525   void (*draw_border_function)(void) = NULL;
526   Bitmap *bitmap = (fade_mode == FADE_MODE_CROSSFADE ? bitmap_db_cross : NULL);
527   int x, y, width, height;
528   int fade_delay, post_delay;
529
530   if (fade_mask & REDRAW_FIELD)
531   {
532     x = REAL_SX;
533     y = REAL_SY;
534     width  = FULL_SXSIZE;
535     height = FULL_SYSIZE;
536
537     fade_delay = menu.fade_delay;
538     post_delay = (fade_mode == FADE_MODE_FADE_OUT ? menu.post_delay : 0);
539
540     draw_border_function = DrawMaskedBorder_FIELD;
541   }
542   else          /* REDRAW_ALL */
543   {
544     x = 0;
545     y = 0;
546     width  = WIN_XSIZE;
547     height = WIN_YSIZE;
548
549     fade_delay = title.fade_delay_final;
550     post_delay = (fade_mode == FADE_MODE_FADE_OUT ? title.post_delay_final : 0);
551   }
552
553   redraw_mask |= fade_mask;
554
555   if (!setup.fade_screens || fade_delay == 0)
556   {
557     if (fade_mode == FADE_MODE_FADE_OUT)
558       ClearRectangle(backbuffer, x, y, width, height);
559
560     BackToFront();
561
562     return;
563   }
564
565   FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
566                 draw_border_function);
567
568   redraw_mask &= ~fade_mask;
569 }
570
571 void FadeIn(int fade_mask)
572 {
573   FadeExt(fade_mask, FADE_MODE_FADE_IN);
574 }
575
576 void FadeOut(int fade_mask)
577 {
578   FadeExt(fade_mask, FADE_MODE_FADE_OUT);
579 }
580
581 void FadeCross(int fade_mask)
582 {
583   FadeExt(fade_mask, FADE_MODE_CROSSFADE);
584 }
585
586 void FadeCrossSaveBackbuffer()
587 {
588   BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
589 }
590
591 void SetWindowBackgroundImageIfDefined(int graphic)
592 {
593   if (graphic_info[graphic].bitmap)
594     SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
595 }
596
597 void SetMainBackgroundImageIfDefined(int graphic)
598 {
599   if (graphic_info[graphic].bitmap)
600     SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
601 }
602
603 void SetMainBackgroundImage(int graphic)
604 {
605   SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
606                           graphic_info[graphic].bitmap ?
607                           graphic_info[graphic].bitmap :
608                           graphic_info[IMG_BACKGROUND].bitmap);
609 }
610
611 void SetDoorBackgroundImage(int graphic)
612 {
613   SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
614                           graphic_info[graphic].bitmap ?
615                           graphic_info[graphic].bitmap :
616                           graphic_info[IMG_BACKGROUND].bitmap);
617 }
618
619 void SetPanelBackground()
620 {
621   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, bitmap_db_panel,
622              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, 0, 0);
623
624   SetDoorBackgroundBitmap(bitmap_db_panel);
625 }
626
627 void DrawBackground(int x, int y, int width, int height)
628 {
629   /* !!! "drawto" might still point to playfield buffer here (see below) !!! */
630   /* (when entering hall of fame after playing) */
631 #if 0
632   ClearRectangleOnBackground(drawto, x, y, width, height);
633 #else
634   ClearRectangleOnBackground(backbuffer, x, y, width, height);
635 #endif
636
637   redraw_mask |= REDRAW_FIELD;
638 }
639
640 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
641 {
642   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
643
644   if (font->bitmap == NULL)
645     return;
646
647   DrawBackground(x, y, width, height);
648 }
649
650 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
651 {
652   struct GraphicInfo *g = &graphic_info[graphic];
653
654   if (g->bitmap == NULL)
655     return;
656
657   DrawBackground(x, y, width, height);
658 }
659
660 void ClearWindow()
661 {
662   /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
663   /* (when entering hall of fame after playing) */
664   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
665
666   /* !!! maybe this should be done before clearing the background !!! */
667   if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
668   {
669     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
670     SetDrawtoField(DRAW_BUFFERED);
671   }
672   else
673     SetDrawtoField(DRAW_BACKBUFFER);
674
675   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
676   {
677     ClearRectangle(window, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
678     SetDrawtoField(DRAW_DIRECT);
679   }
680 }
681
682 void MarkTileDirty(int x, int y)
683 {
684   int xx = redraw_x1 + x;
685   int yy = redraw_y1 + y;
686
687   if (!redraw[xx][yy])
688     redraw_tiles++;
689
690   redraw[xx][yy] = TRUE;
691   redraw_mask |= REDRAW_TILES;
692 }
693
694 void SetBorderElement()
695 {
696   int x, y;
697
698   BorderElement = EL_EMPTY;
699
700   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
701   {
702     for (x = 0; x < lev_fieldx; x++)
703     {
704       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
705         BorderElement = EL_STEELWALL;
706
707       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
708         x = lev_fieldx - 2;
709     }
710   }
711 }
712
713 void FloodFillLevel(int from_x, int from_y, int fill_element,
714                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
715                     int max_fieldx, int max_fieldy)
716 {
717   int i,x,y;
718   int old_element;
719   static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
720   static int safety = 0;
721
722   /* check if starting field still has the desired content */
723   if (field[from_x][from_y] == fill_element)
724     return;
725
726   safety++;
727
728   if (safety > max_fieldx * max_fieldy)
729     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
730
731   old_element = field[from_x][from_y];
732   field[from_x][from_y] = fill_element;
733
734   for (i = 0; i < 4; i++)
735   {
736     x = from_x + check[i][0];
737     y = from_y + check[i][1];
738
739     if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
740       FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
741   }
742
743   safety--;
744 }
745
746 void SetRandomAnimationValue(int x, int y)
747 {
748   gfx.anim_random_frame = GfxRandom[x][y];
749 }
750
751 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
752 {
753   /* animation synchronized with global frame counter, not move position */
754   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
755     sync_frame = FrameCounter;
756
757   return getAnimationFrame(graphic_info[graphic].anim_frames,
758                            graphic_info[graphic].anim_delay,
759                            graphic_info[graphic].anim_mode,
760                            graphic_info[graphic].anim_start_frame,
761                            sync_frame);
762 }
763
764 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
765                                 int *x, int *y, boolean get_backside)
766 {
767   struct GraphicInfo *g = &graphic_info[graphic];
768   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
769   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
770
771   *bitmap = g->bitmap;
772
773   if (g->offset_y == 0)         /* frames are ordered horizontally */
774   {
775     int max_width = g->anim_frames_per_line * g->width;
776     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
777
778     *x = pos % max_width;
779     *y = src_y % g->height + pos / max_width * g->height;
780   }
781   else if (g->offset_x == 0)    /* frames are ordered vertically */
782   {
783     int max_height = g->anim_frames_per_line * g->height;
784     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
785
786     *x = src_x % g->width + pos / max_height * g->width;
787     *y = pos % max_height;
788   }
789   else                          /* frames are ordered diagonally */
790   {
791     *x = src_x + frame * g->offset_x;
792     *y = src_y + frame * g->offset_y;
793   }
794 }
795
796 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
797 {
798   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
799 }
800
801 void DrawGraphic(int x, int y, int graphic, int frame)
802 {
803 #if DEBUG
804   if (!IN_SCR_FIELD(x, y))
805   {
806     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
807     printf("DrawGraphic(): This should never happen!\n");
808     return;
809   }
810 #endif
811
812   DrawGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic, frame);
813   MarkTileDirty(x, y);
814 }
815
816 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
817                     int frame)
818 {
819   Bitmap *src_bitmap;
820   int src_x, src_y;
821
822   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
823   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
824 }
825
826 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
827 {
828 #if DEBUG
829   if (!IN_SCR_FIELD(x, y))
830   {
831     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
832     printf("DrawGraphicThruMask(): This should never happen!\n");
833     return;
834   }
835 #endif
836
837   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y *TILEY, graphic,
838                          frame);
839   MarkTileDirty(x, y);
840 }
841
842 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
843                             int frame)
844 {
845   Bitmap *src_bitmap;
846   int src_x, src_y;
847
848   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
849
850   SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
851                 dst_x - src_x, dst_y - src_y);
852   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dst_x, dst_y);
853 }
854
855 void DrawMiniGraphic(int x, int y, int graphic)
856 {
857   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
858   MarkTileDirty(x / 2, y / 2);
859 }
860
861 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
862 {
863   struct GraphicInfo *g = &graphic_info[graphic];
864   int mini_startx = 0;
865   int mini_starty = g->bitmap->height * 2 / 3;
866
867   *bitmap = g->bitmap;
868   *x = mini_startx + g->src_x / 2;
869   *y = mini_starty + g->src_y / 2;
870 }
871
872 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
873 {
874   Bitmap *src_bitmap;
875   int src_x, src_y;
876
877   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
878   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
879 }
880
881 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
882                                             int graphic, int frame,
883                                             int cut_mode, int mask_mode)
884 {
885   Bitmap *src_bitmap;
886   int src_x, src_y;
887   int dst_x, dst_y;
888   int width = TILEX, height = TILEY;
889   int cx = 0, cy = 0;
890
891   if (dx || dy)                 /* shifted graphic */
892   {
893     if (x < BX1)                /* object enters playfield from the left */
894     {
895       x = BX1;
896       width = dx;
897       cx = TILEX - dx;
898       dx = 0;
899     }
900     else if (x > BX2)           /* object enters playfield from the right */
901     {
902       x = BX2;
903       width = -dx;
904       dx = TILEX + dx;
905     }
906     else if (x==BX1 && dx < 0)  /* object leaves playfield to the left */
907     {
908       width += dx;
909       cx = -dx;
910       dx = 0;
911     }
912     else if (x==BX2 && dx > 0)  /* object leaves playfield to the right */
913       width -= dx;
914     else if (dx)                /* general horizontal movement */
915       MarkTileDirty(x + SIGN(dx), y);
916
917     if (y < BY1)                /* object enters playfield from the top */
918     {
919       if (cut_mode==CUT_BELOW)  /* object completely above top border */
920         return;
921
922       y = BY1;
923       height = dy;
924       cy = TILEY - dy;
925       dy = 0;
926     }
927     else if (y > BY2)           /* object enters playfield from the bottom */
928     {
929       y = BY2;
930       height = -dy;
931       dy = TILEY + dy;
932     }
933     else if (y==BY1 && dy < 0)  /* object leaves playfield to the top */
934     {
935       height += dy;
936       cy = -dy;
937       dy = 0;
938     }
939     else if (dy > 0 && cut_mode == CUT_ABOVE)
940     {
941       if (y == BY2)             /* object completely above bottom border */
942         return;
943
944       height = dy;
945       cy = TILEY - dy;
946       dy = TILEY;
947       MarkTileDirty(x, y + 1);
948     }                           /* object leaves playfield to the bottom */
949     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
950       height -= dy;
951     else if (dy)                /* general vertical movement */
952       MarkTileDirty(x, y + SIGN(dy));
953   }
954
955 #if DEBUG
956   if (!IN_SCR_FIELD(x, y))
957   {
958     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
959     printf("DrawGraphicShifted(): This should never happen!\n");
960     return;
961   }
962 #endif
963
964   if (width > 0 && height > 0)
965   {
966     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
967
968     src_x += cx;
969     src_y += cy;
970
971     dst_x = FX + x * TILEX + dx;
972     dst_y = FY + y * TILEY + dy;
973
974     if (mask_mode == USE_MASKING)
975     {
976       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
977                     dst_x - src_x, dst_y - src_y);
978       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
979                        dst_x, dst_y);
980     }
981     else
982       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
983                  dst_x, dst_y);
984
985     MarkTileDirty(x, y);
986   }
987 }
988
989 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
990                                             int graphic, int frame,
991                                             int cut_mode, int mask_mode)
992 {
993   Bitmap *src_bitmap;
994   int src_x, src_y;
995   int dst_x, dst_y;
996   int width = TILEX, height = TILEY;
997   int x1 = x;
998   int y1 = y;
999   int x2 = x + SIGN(dx);
1000   int y2 = y + SIGN(dy);
1001   int anim_frames = graphic_info[graphic].anim_frames;
1002   int sync_frame = (dx ? ABS(dx) : ABS(dy)) * anim_frames / TILESIZE;
1003   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    /* only for falling! */
1004   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    /* only for falling! */
1005
1006   /* re-calculate animation frame for two-tile movement animation */
1007   frame = getGraphicAnimationFrame(graphic, sync_frame);
1008
1009   /* check if movement start graphic inside screen area and should be drawn */
1010   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1011   {
1012     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1013
1014     dst_x = FX + x1 * TILEX;
1015     dst_y = FY + y1 * TILEY;
1016
1017     if (mask_mode == USE_MASKING)
1018     {
1019       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1020                     dst_x - src_x, dst_y - src_y);
1021       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1022                        dst_x, dst_y);
1023     }
1024     else
1025       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1026                  dst_x, dst_y);
1027
1028     MarkTileDirty(x1, y1);
1029   }
1030
1031   /* check if movement end graphic inside screen area and should be drawn */
1032   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1033   {
1034     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1035
1036     dst_x = FX + x2 * TILEX;
1037     dst_y = FY + y2 * TILEY;
1038
1039     if (mask_mode == USE_MASKING)
1040     {
1041       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1042                     dst_x - src_x, dst_y - src_y);
1043       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1044                        dst_x, dst_y);
1045     }
1046     else
1047       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1048                  dst_x, dst_y);
1049
1050     MarkTileDirty(x2, y2);
1051   }
1052 }
1053
1054 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1055                                int graphic, int frame,
1056                                int cut_mode, int mask_mode)
1057 {
1058   if (graphic < 0)
1059   {
1060     DrawGraphic(x, y, graphic, frame);
1061
1062     return;
1063   }
1064
1065   if (graphic_info[graphic].double_movement)    /* EM style movement images */
1066     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1067   else
1068     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1069 }
1070
1071 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1072                                 int frame, int cut_mode)
1073 {
1074   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1075 }
1076
1077 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1078                           int cut_mode, int mask_mode)
1079 {
1080   int lx = LEVELX(x), ly = LEVELY(y);
1081   int graphic;
1082   int frame;
1083
1084   if (IN_LEV_FIELD(lx, ly))
1085   {
1086     SetRandomAnimationValue(lx, ly);
1087
1088     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1089     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1090
1091     /* do not use double (EM style) movement graphic when not moving */
1092     if (graphic_info[graphic].double_movement && !dx && !dy)
1093     {
1094       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1095       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1096     }
1097   }
1098   else  /* border element */
1099   {
1100     graphic = el2img(element);
1101     frame = getGraphicAnimationFrame(graphic, -1);
1102   }
1103
1104   if (element == EL_EXPANDABLE_WALL)
1105   {
1106     boolean left_stopped = FALSE, right_stopped = FALSE;
1107
1108     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1109       left_stopped = TRUE;
1110     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1111       right_stopped = TRUE;
1112
1113     if (left_stopped && right_stopped)
1114       graphic = IMG_WALL;
1115     else if (left_stopped)
1116     {
1117       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1118       frame = graphic_info[graphic].anim_frames - 1;
1119     }
1120     else if (right_stopped)
1121     {
1122       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1123       frame = graphic_info[graphic].anim_frames - 1;
1124     }
1125   }
1126
1127   if (dx || dy)
1128     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1129   else if (mask_mode == USE_MASKING)
1130     DrawGraphicThruMask(x, y, graphic, frame);
1131   else
1132     DrawGraphic(x, y, graphic, frame);
1133 }
1134
1135 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1136                          int cut_mode, int mask_mode)
1137 {
1138   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1139     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1140                          cut_mode, mask_mode);
1141 }
1142
1143 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1144                               int cut_mode)
1145 {
1146   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1147 }
1148
1149 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1150                              int cut_mode)
1151 {
1152   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1153 }
1154
1155 void DrawLevelElementThruMask(int x, int y, int element)
1156 {
1157   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1158 }
1159
1160 void DrawLevelFieldThruMask(int x, int y)
1161 {
1162   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1163 }
1164
1165 static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
1166 {
1167   Bitmap *src_bitmap;
1168   int src_x, src_y;
1169   int sx = SCREENX(x), sy = SCREENY(y);
1170   int element;
1171   int width, height, cx, cy, i;
1172   int crumbled_border_size = graphic_info[graphic].border_size;
1173   static int xy[4][2] =
1174   {
1175     { 0, -1 },
1176     { -1, 0 },
1177     { +1, 0 },
1178     { 0, +1 }
1179   };
1180
1181   if (!IN_LEV_FIELD(x, y))
1182     return;
1183
1184   element = TILE_GFX_ELEMENT(x, y);
1185
1186   /* crumble field itself */
1187   if (GFX_CRUMBLED(element) && !IS_MOVING(x, y))
1188   {
1189     if (!IN_SCR_FIELD(sx, sy))
1190       return;
1191
1192     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1193
1194     for (i = 0; i < 4; i++)
1195     {
1196       int xx = x + xy[i][0];
1197       int yy = y + xy[i][1];
1198
1199       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1200                  BorderElement);
1201
1202       /* check if neighbour field is of same type */
1203       if (GFX_CRUMBLED(element) && !IS_MOVING(xx, yy))
1204         continue;
1205
1206       if (i == 1 || i == 2)
1207       {
1208         width = crumbled_border_size;
1209         height = TILEY;
1210         cx = (i == 2 ? TILEX - crumbled_border_size : 0);
1211         cy = 0;
1212       }
1213       else
1214       {
1215         width = TILEX;
1216         height = crumbled_border_size;
1217         cx = 0;
1218         cy = (i == 3 ? TILEY - crumbled_border_size : 0);
1219       }
1220
1221       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1222                  width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
1223     }
1224
1225     MarkTileDirty(sx, sy);
1226   }
1227   else          /* crumble neighbour fields */
1228   {
1229     for (i = 0; i < 4; i++)
1230     {
1231       int xx = x + xy[i][0];
1232       int yy = y + xy[i][1];
1233       int sxx = sx + xy[i][0];
1234       int syy = sy + xy[i][1];
1235
1236       if (!IN_LEV_FIELD(xx, yy) ||
1237           !IN_SCR_FIELD(sxx, syy) ||
1238           IS_MOVING(xx, yy))
1239         continue;
1240
1241       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1242         continue;
1243
1244       element = TILE_GFX_ELEMENT(xx, yy);
1245
1246       if (!GFX_CRUMBLED(element))
1247         continue;
1248
1249       graphic = el_act2crm(element, ACTION_DEFAULT);
1250       crumbled_border_size = graphic_info[graphic].border_size;
1251
1252       getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1253
1254       if (i == 1 || i == 2)
1255       {
1256         width = crumbled_border_size;
1257         height = TILEY;
1258         cx = (i == 1 ? TILEX - crumbled_border_size : 0);
1259         cy = 0;
1260       }
1261       else
1262       {
1263         width = TILEX;
1264         height = crumbled_border_size;
1265         cx = 0;
1266         cy = (i == 0 ? TILEY - crumbled_border_size : 0);
1267       }
1268
1269       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1270                  width, height, FX + sxx * TILEX + cx, FY + syy * TILEY + cy);
1271
1272       MarkTileDirty(sxx, syy);
1273     }
1274   }
1275 }
1276
1277 void DrawLevelFieldCrumbledSand(int x, int y)
1278 {
1279   int graphic;
1280
1281   if (!IN_LEV_FIELD(x, y))
1282     return;
1283
1284 #if 1
1285   /* !!! CHECK THIS !!! */
1286
1287   /*
1288   if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1289       GFX_CRUMBLED(GfxElement[x][y]))
1290   */
1291
1292   if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1293       GfxElement[x][y] != EL_UNDEFINED &&
1294       GFX_CRUMBLED(GfxElement[x][y]))
1295   {
1296     DrawLevelFieldCrumbledSandDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1297
1298     return;
1299   }
1300 #endif
1301
1302 #if 1
1303   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1304 #else
1305   graphic = el_act2crm(Feld[x][y], ACTION_DEFAULT);
1306 #endif
1307
1308   DrawLevelFieldCrumbledSandExt(x, y, graphic, 0);
1309 }
1310
1311 void DrawLevelFieldCrumbledSandDigging(int x, int y, int direction,
1312                                        int step_frame)
1313 {
1314   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1315   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1316   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1317   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1318   int sx = SCREENX(x), sy = SCREENY(y);
1319
1320   DrawGraphic(sx, sy, graphic1, frame1);
1321   DrawLevelFieldCrumbledSandExt(x, y, graphic2, frame2);
1322 }
1323
1324 void DrawLevelFieldCrumbledSandNeighbours(int x, int y)
1325 {
1326   int sx = SCREENX(x), sy = SCREENY(y);
1327   static int xy[4][2] =
1328   {
1329     { 0, -1 },
1330     { -1, 0 },
1331     { +1, 0 },
1332     { 0, +1 }
1333   };
1334   int i;
1335
1336   for (i = 0; i < 4; i++)
1337   {
1338     int xx = x + xy[i][0];
1339     int yy = y + xy[i][1];
1340     int sxx = sx + xy[i][0];
1341     int syy = sy + xy[i][1];
1342
1343     if (!IN_LEV_FIELD(xx, yy) ||
1344         !IN_SCR_FIELD(sxx, syy) ||
1345         !GFX_CRUMBLED(Feld[xx][yy]) ||
1346         IS_MOVING(xx, yy))
1347       continue;
1348
1349     DrawLevelField(xx, yy);
1350   }
1351 }
1352
1353 static int getBorderElement(int x, int y)
1354 {
1355   int border[7][2] =
1356   {
1357     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
1358     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
1359     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
1360     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1361     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
1362     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
1363     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
1364   };
1365   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1366   int steel_position = (x == -1         && y == -1              ? 0 :
1367                         x == lev_fieldx && y == -1              ? 1 :
1368                         x == -1         && y == lev_fieldy      ? 2 :
1369                         x == lev_fieldx && y == lev_fieldy      ? 3 :
1370                         x == -1         || x == lev_fieldx      ? 4 :
1371                         y == -1         || y == lev_fieldy      ? 5 : 6);
1372
1373   return border[steel_position][steel_type];
1374 }
1375
1376 void DrawScreenElement(int x, int y, int element)
1377 {
1378   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1379   DrawLevelFieldCrumbledSand(LEVELX(x), LEVELY(y));
1380 }
1381
1382 void DrawLevelElement(int x, int y, int element)
1383 {
1384   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1385     DrawScreenElement(SCREENX(x), SCREENY(y), element);
1386 }
1387
1388 void DrawScreenField(int x, int y)
1389 {
1390   int lx = LEVELX(x), ly = LEVELY(y);
1391   int element, content;
1392
1393   if (!IN_LEV_FIELD(lx, ly))
1394   {
1395     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1396       element = EL_EMPTY;
1397     else
1398       element = getBorderElement(lx, ly);
1399
1400     DrawScreenElement(x, y, element);
1401     return;
1402   }
1403
1404   element = Feld[lx][ly];
1405   content = Store[lx][ly];
1406
1407   if (IS_MOVING(lx, ly))
1408   {
1409     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1410     boolean cut_mode = NO_CUTTING;
1411
1412     if (element == EL_QUICKSAND_EMPTYING ||
1413         element == EL_QUICKSAND_FAST_EMPTYING ||
1414         element == EL_MAGIC_WALL_EMPTYING ||
1415         element == EL_BD_MAGIC_WALL_EMPTYING ||
1416         element == EL_DC_MAGIC_WALL_EMPTYING ||
1417         element == EL_AMOEBA_DROPPING)
1418       cut_mode = CUT_ABOVE;
1419     else if (element == EL_QUICKSAND_FILLING ||
1420              element == EL_QUICKSAND_FAST_FILLING ||
1421              element == EL_MAGIC_WALL_FILLING ||
1422              element == EL_BD_MAGIC_WALL_FILLING ||
1423              element == EL_DC_MAGIC_WALL_FILLING)
1424       cut_mode = CUT_BELOW;
1425
1426     if (cut_mode == CUT_ABOVE)
1427       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
1428     else
1429       DrawScreenElement(x, y, EL_EMPTY);
1430
1431     if (horiz_move)
1432       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1433     else if (cut_mode == NO_CUTTING)
1434       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1435     else
1436       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1437
1438     if (content == EL_ACID)
1439     {
1440       int dir = MovDir[lx][ly];
1441       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1442       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
1443
1444       DrawLevelElementThruMask(newlx, newly, EL_ACID);
1445     }
1446   }
1447   else if (IS_BLOCKED(lx, ly))
1448   {
1449     int oldx, oldy;
1450     int sx, sy;
1451     int horiz_move;
1452     boolean cut_mode = NO_CUTTING;
1453     int element_old, content_old;
1454
1455     Blocked2Moving(lx, ly, &oldx, &oldy);
1456     sx = SCREENX(oldx);
1457     sy = SCREENY(oldy);
1458     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1459                   MovDir[oldx][oldy] == MV_RIGHT);
1460
1461     element_old = Feld[oldx][oldy];
1462     content_old = Store[oldx][oldy];
1463
1464     if (element_old == EL_QUICKSAND_EMPTYING ||
1465         element_old == EL_QUICKSAND_FAST_EMPTYING ||
1466         element_old == EL_MAGIC_WALL_EMPTYING ||
1467         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1468         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1469         element_old == EL_AMOEBA_DROPPING)
1470       cut_mode = CUT_ABOVE;
1471
1472     DrawScreenElement(x, y, EL_EMPTY);
1473
1474     if (horiz_move)
1475       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1476                                NO_CUTTING);
1477     else if (cut_mode == NO_CUTTING)
1478       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1479                                cut_mode);
1480     else
1481       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1482                                cut_mode);
1483   }
1484   else if (IS_DRAWABLE(element))
1485     DrawScreenElement(x, y, element);
1486   else
1487     DrawScreenElement(x, y, EL_EMPTY);
1488 }
1489
1490 void DrawLevelField(int x, int y)
1491 {
1492   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1493     DrawScreenField(SCREENX(x), SCREENY(y));
1494   else if (IS_MOVING(x, y))
1495   {
1496     int newx,newy;
1497
1498     Moving2Blocked(x, y, &newx, &newy);
1499     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1500       DrawScreenField(SCREENX(newx), SCREENY(newy));
1501   }
1502   else if (IS_BLOCKED(x, y))
1503   {
1504     int oldx, oldy;
1505
1506     Blocked2Moving(x, y, &oldx, &oldy);
1507     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1508       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1509   }
1510 }
1511
1512 void DrawMiniElement(int x, int y, int element)
1513 {
1514   int graphic;
1515
1516   graphic = el2edimg(element);
1517   DrawMiniGraphic(x, y, graphic);
1518 }
1519
1520 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
1521 {
1522   int x = sx + scroll_x, y = sy + scroll_y;
1523
1524   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1525     DrawMiniElement(sx, sy, EL_EMPTY);
1526   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1527     DrawMiniElement(sx, sy, Feld[x][y]);
1528   else
1529     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
1530 }
1531
1532 void DrawEnvelopeBackground(int envelope_nr, int startx, int starty,
1533                             int x, int y, int xsize, int ysize, int font_nr)
1534 {
1535   int font_width  = getFontWidth(font_nr);
1536   int font_height = getFontHeight(font_nr);
1537   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1538   Bitmap *src_bitmap;
1539   int src_x, src_y;
1540   int dst_x = SX + startx + x * font_width;
1541   int dst_y = SY + starty + y * font_height;
1542   int width  = graphic_info[graphic].width;
1543   int height = graphic_info[graphic].height;
1544   int inner_width  = MAX(width  - 2 * font_width,  font_width);
1545   int inner_height = MAX(height - 2 * font_height, font_height);
1546   int inner_sx = (width >= 3 * font_width ? font_width : 0);
1547   int inner_sy = (height >= 3 * font_height ? font_height : 0);
1548   boolean draw_masked = graphic_info[graphic].draw_masked;
1549
1550   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1551
1552   if (src_bitmap == NULL || width < font_width || height < font_height)
1553   {
1554     ClearRectangle(drawto, dst_x, dst_y, font_width, font_height);
1555     return;
1556   }
1557
1558   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - font_width  :
1559             inner_sx + (x - 1) * font_width  % inner_width);
1560   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - font_height :
1561             inner_sy + (y - 1) * font_height % inner_height);
1562
1563   if (draw_masked)
1564   {
1565     SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1566                   dst_x - src_x, dst_y - src_y);
1567     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1568                      dst_x, dst_y);
1569   }
1570   else
1571     BlitBitmap(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1572                dst_x, dst_y);
1573 }
1574
1575 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
1576 {
1577   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1578   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1579   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
1580   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1581   boolean no_delay = (tape.warp_forward);
1582   unsigned long anim_delay = 0;
1583   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
1584   int anim_delay_value = (no_delay ? 0 : frame_delay_value);
1585   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
1586   int font_width = getFontWidth(font_nr);
1587   int font_height = getFontHeight(font_nr);
1588   int max_xsize = level.envelope[envelope_nr].xsize;
1589   int max_ysize = level.envelope[envelope_nr].ysize;
1590   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
1591   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
1592   int xend = max_xsize;
1593   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
1594   int xstep = (xstart < xend ? 1 : 0);
1595   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
1596   int x, y;
1597
1598   for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
1599   {
1600     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
1601     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
1602     int sx = (SXSIZE - xsize * font_width)  / 2;
1603     int sy = (SYSIZE - ysize * font_height) / 2;
1604     int xx, yy;
1605
1606     SetDrawtoField(DRAW_BUFFERED);
1607
1608     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1609
1610     SetDrawtoField(DRAW_BACKBUFFER);
1611
1612     for (yy = 0; yy < ysize; yy++) for (xx = 0; xx < xsize; xx++)
1613       DrawEnvelopeBackground(envelope_nr, sx,sy, xx,yy, xsize, ysize, font_nr);
1614
1615     DrawTextToTextArea(SX + sx + font_width, SY + sy + font_height,
1616                        level.envelope[envelope_nr].text, font_nr, max_xsize,
1617                        xsize - 2, ysize - 2, mask_mode);
1618
1619     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1620     BackToFront();
1621
1622     WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
1623   }
1624 }
1625
1626 void ShowEnvelope(int envelope_nr)
1627 {
1628   int element = EL_ENVELOPE_1 + envelope_nr;
1629   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1630   int sound_opening = element_info[element].sound[ACTION_OPENING];
1631   int sound_closing = element_info[element].sound[ACTION_CLOSING];
1632   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1633   boolean no_delay = (tape.warp_forward);
1634   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
1635   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
1636   int anim_mode = graphic_info[graphic].anim_mode;
1637   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
1638                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
1639
1640   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
1641
1642   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
1643
1644   if (anim_mode == ANIM_DEFAULT)
1645     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
1646
1647   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
1648
1649   if (tape.playing)
1650     Delay(wait_delay_value);
1651   else
1652     WaitForEventToContinue();
1653
1654   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
1655
1656   if (anim_mode != ANIM_NONE)
1657     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
1658
1659   if (anim_mode == ANIM_DEFAULT)
1660     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
1661
1662   game.envelope_active = FALSE;
1663
1664   SetDrawtoField(DRAW_BUFFERED);
1665
1666   redraw_mask |= REDRAW_FIELD;
1667   BackToFront();
1668 }
1669
1670 void getPreviewGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y,
1671                              int tilesize)
1672 {
1673   struct
1674   {
1675     int width_mult, width_div;
1676     int height_mult, height_div;
1677   } offset_calc[4] =
1678   {
1679     { 0, 1,     0, 1    },
1680     { 0, 1,     2, 3    },
1681     { 1, 2,     2, 3    },
1682     { 3, 4,     2, 3    },
1683   };
1684   int offset_calc_pos = (tilesize < MICRO_TILESIZE || tilesize > TILESIZE ? 3 :
1685                          5 - log_2(tilesize));
1686   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1687   int width_mult = offset_calc[offset_calc_pos].width_mult;
1688   int width_div = offset_calc[offset_calc_pos].width_div;
1689   int height_mult = offset_calc[offset_calc_pos].height_mult;
1690   int height_div = offset_calc[offset_calc_pos].height_div;
1691   int mini_startx = src_bitmap->width * width_mult / width_div;
1692   int mini_starty = src_bitmap->height * height_mult / height_div;
1693   int src_x = mini_startx + graphic_info[graphic].src_x * tilesize / TILESIZE;
1694   int src_y = mini_starty + graphic_info[graphic].src_y * tilesize / TILESIZE;
1695
1696   *bitmap = src_bitmap;
1697   *x = src_x;
1698   *y = src_y;
1699 }
1700
1701 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
1702 {
1703   Bitmap *src_bitmap;
1704   int src_x, src_y;
1705   int graphic = el2preimg(element);
1706
1707   getPreviewGraphicSource(graphic, &src_bitmap, &src_x, &src_y, tilesize);
1708   BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
1709 }
1710
1711 void DrawLevel()
1712 {
1713   int x,y;
1714
1715   SetDrawBackgroundMask(REDRAW_NONE);
1716   ClearWindow();
1717
1718   for (x = BX1; x <= BX2; x++)
1719     for (y = BY1; y <= BY2; y++)
1720       DrawScreenField(x, y);
1721
1722   redraw_mask |= REDRAW_FIELD;
1723 }
1724
1725 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
1726 {
1727   int x,y;
1728
1729   for (x = 0; x < size_x; x++)
1730     for (y = 0; y < size_y; y++)
1731       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
1732
1733   redraw_mask |= REDRAW_FIELD;
1734 }
1735
1736 static void DrawPreviewLevelExt(int from_x, int from_y)
1737 {
1738   boolean show_level_border = (BorderElement != EL_EMPTY);
1739   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
1740   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
1741   int tile_size = preview.tile_size;
1742   int preview_width  = preview.xsize * tile_size;
1743   int preview_height = preview.ysize * tile_size;
1744   int real_preview_xsize = MIN(level_xsize, preview.xsize);
1745   int real_preview_ysize = MIN(level_ysize, preview.ysize);
1746   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
1747   int dst_y = SY + preview.y;
1748   int x, y;
1749
1750   DrawBackground(dst_x, dst_y, preview_width, preview_height);
1751
1752   dst_x += (preview_width  - real_preview_xsize * tile_size) / 2;
1753   dst_y += (preview_height - real_preview_ysize * tile_size) / 2;
1754
1755   for (x = 0; x < real_preview_xsize; x++)
1756   {
1757     for (y = 0; y < real_preview_ysize; y++)
1758     {
1759       int lx = from_x + x + (show_level_border ? -1 : 0);
1760       int ly = from_y + y + (show_level_border ? -1 : 0);
1761       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
1762                      getBorderElement(lx, ly));
1763
1764       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
1765                          element, tile_size);
1766     }
1767   }
1768
1769   redraw_mask |= REDRAW_MICROLEVEL;
1770 }
1771
1772 #define MICROLABEL_EMPTY                0
1773 #define MICROLABEL_LEVEL_NAME           1
1774 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
1775 #define MICROLABEL_LEVEL_AUTHOR         3
1776 #define MICROLABEL_IMPORTED_FROM_HEAD   4
1777 #define MICROLABEL_IMPORTED_FROM        5
1778 #define MICROLABEL_IMPORTED_BY_HEAD     6
1779 #define MICROLABEL_IMPORTED_BY          7
1780
1781 static int getMaxTextLength(struct MenuPosInfo *pos, int font_nr)
1782 {
1783   int max_text_width = SXSIZE;
1784   int font_width = getFontWidth(font_nr);
1785
1786   if (pos->align == ALIGN_CENTER)
1787     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
1788   else if (pos->align == ALIGN_RIGHT)
1789     max_text_width = pos->x;
1790   else
1791     max_text_width = SXSIZE - pos->x;
1792
1793   return max_text_width / font_width;
1794 }
1795
1796 static void DrawPreviewLevelLabelExt(int mode)
1797 {
1798   struct MenuPosInfo *pos = &menu.main.text.level_info_2;
1799   char label_text[MAX_OUTPUT_LINESIZE + 1];
1800   int max_len_label_text;
1801   int font_nr = FONT_TEXT_2;
1802   int i;
1803
1804   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
1805       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
1806       mode == MICROLABEL_IMPORTED_BY_HEAD)
1807     font_nr = FONT_TEXT_3;
1808
1809 #if 1
1810   max_len_label_text = getMaxTextLength(pos, font_nr);
1811 #else
1812   max_len_label_text = SXSIZE / getFontWidth(font_nr);
1813 #endif
1814
1815   for (i = 0; i < max_len_label_text; i++)
1816     label_text[i] = ' ';
1817   label_text[max_len_label_text] = '\0';
1818
1819   if (strlen(label_text) > 0)
1820   {
1821 #if 1
1822     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
1823 #else
1824     int lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
1825     int lypos = MICROLABEL2_YPOS;
1826
1827     DrawText(lxpos, lypos, label_text, font_nr);
1828 #endif
1829   }
1830
1831   strncpy(label_text,
1832           (mode == MICROLABEL_LEVEL_NAME ? level.name :
1833            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
1834            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
1835            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
1836            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
1837            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
1838            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
1839           max_len_label_text);
1840   label_text[max_len_label_text] = '\0';
1841
1842   if (strlen(label_text) > 0)
1843   {
1844 #if 1
1845     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
1846 #else
1847     int lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
1848     int lypos = MICROLABEL2_YPOS;
1849
1850     DrawText(lxpos, lypos, label_text, font_nr);
1851 #endif
1852   }
1853
1854   redraw_mask |= REDRAW_MICROLEVEL;
1855 }
1856
1857 void DrawPreviewLevel(boolean restart)
1858 {
1859   static unsigned long scroll_delay = 0;
1860   static unsigned long label_delay = 0;
1861   static int from_x, from_y, scroll_direction;
1862   static int label_state, label_counter;
1863   unsigned long scroll_delay_value = preview.step_delay;
1864   boolean show_level_border = (BorderElement != EL_EMPTY);
1865   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
1866   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
1867   int last_game_status = game_status;           /* save current game status */
1868
1869   /* force PREVIEW font on preview level */
1870   game_status = GAME_MODE_PSEUDO_PREVIEW;
1871
1872   if (restart)
1873   {
1874     from_x = 0;
1875     from_y = 0;
1876
1877     if (preview.anim_mode == ANIM_CENTERED)
1878     {
1879       if (level_xsize > preview.xsize)
1880         from_x = (level_xsize - preview.xsize) / 2;
1881       if (level_ysize > preview.ysize)
1882         from_y = (level_ysize - preview.ysize) / 2;
1883     }
1884
1885     from_x += preview.xoffset;
1886     from_y += preview.yoffset;
1887
1888     scroll_direction = MV_RIGHT;
1889     label_state = 1;
1890     label_counter = 0;
1891
1892     DrawPreviewLevelExt(from_x, from_y);
1893     DrawPreviewLevelLabelExt(label_state);
1894
1895     /* initialize delay counters */
1896     DelayReached(&scroll_delay, 0);
1897     DelayReached(&label_delay, 0);
1898
1899     if (leveldir_current->name)
1900     {
1901       struct MenuPosInfo *pos = &menu.main.text.level_info_1;
1902       char label_text[MAX_OUTPUT_LINESIZE + 1];
1903       int font_nr = FONT_TEXT_1;
1904 #if 1
1905       int max_len_label_text = getMaxTextLength(pos, font_nr);
1906 #else
1907       int max_len_label_text = SXSIZE / getFontWidth(font_nr);
1908 #endif
1909 #if 0
1910       int text_width;
1911       int lxpos, lypos;
1912 #endif
1913
1914       strncpy(label_text, leveldir_current->name, max_len_label_text);
1915       label_text[max_len_label_text] = '\0';
1916
1917 #if 1
1918       DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
1919 #else
1920       lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
1921       lypos = SY + MICROLABEL1_YPOS;
1922
1923       DrawText(lxpos, lypos, label_text, font_nr);
1924 #endif
1925     }
1926
1927     game_status = last_game_status;     /* restore current game status */
1928
1929     return;
1930   }
1931
1932   /* scroll preview level, if needed */
1933   if (preview.anim_mode != ANIM_NONE &&
1934       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
1935       DelayReached(&scroll_delay, scroll_delay_value))
1936   {
1937     switch (scroll_direction)
1938     {
1939       case MV_LEFT:
1940         if (from_x > 0)
1941         {
1942           from_x -= preview.step_offset;
1943           from_x = (from_x < 0 ? 0 : from_x);
1944         }
1945         else
1946           scroll_direction = MV_UP;
1947         break;
1948
1949       case MV_RIGHT:
1950         if (from_x < level_xsize - preview.xsize)
1951         {
1952           from_x += preview.step_offset;
1953           from_x = (from_x > level_xsize - preview.xsize ?
1954                     level_xsize - preview.xsize : from_x);
1955         }
1956         else
1957           scroll_direction = MV_DOWN;
1958         break;
1959
1960       case MV_UP:
1961         if (from_y > 0)
1962         {
1963           from_y -= preview.step_offset;
1964           from_y = (from_y < 0 ? 0 : from_y);
1965         }
1966         else
1967           scroll_direction = MV_RIGHT;
1968         break;
1969
1970       case MV_DOWN:
1971         if (from_y < level_ysize - preview.ysize)
1972         {
1973           from_y += preview.step_offset;
1974           from_y = (from_y > level_ysize - preview.ysize ?
1975                     level_ysize - preview.ysize : from_y);
1976         }
1977         else
1978           scroll_direction = MV_LEFT;
1979         break;
1980
1981       default:
1982         break;
1983     }
1984
1985     DrawPreviewLevelExt(from_x, from_y);
1986   }
1987
1988   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
1989   /* redraw micro level label, if needed */
1990   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
1991       !strEqual(level.author, ANONYMOUS_NAME) &&
1992       !strEqual(level.author, leveldir_current->name) &&
1993       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
1994   {
1995     int max_label_counter = 23;
1996
1997     if (leveldir_current->imported_from != NULL &&
1998         strlen(leveldir_current->imported_from) > 0)
1999       max_label_counter += 14;
2000     if (leveldir_current->imported_by != NULL &&
2001         strlen(leveldir_current->imported_by) > 0)
2002       max_label_counter += 14;
2003
2004     label_counter = (label_counter + 1) % max_label_counter;
2005     label_state = (label_counter >= 0 && label_counter <= 7 ?
2006                    MICROLABEL_LEVEL_NAME :
2007                    label_counter >= 9 && label_counter <= 12 ?
2008                    MICROLABEL_LEVEL_AUTHOR_HEAD :
2009                    label_counter >= 14 && label_counter <= 21 ?
2010                    MICROLABEL_LEVEL_AUTHOR :
2011                    label_counter >= 23 && label_counter <= 26 ?
2012                    MICROLABEL_IMPORTED_FROM_HEAD :
2013                    label_counter >= 28 && label_counter <= 35 ?
2014                    MICROLABEL_IMPORTED_FROM :
2015                    label_counter >= 37 && label_counter <= 40 ?
2016                    MICROLABEL_IMPORTED_BY_HEAD :
2017                    label_counter >= 42 && label_counter <= 49 ?
2018                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2019
2020     if (leveldir_current->imported_from == NULL &&
2021         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2022          label_state == MICROLABEL_IMPORTED_FROM))
2023       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2024                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2025
2026     DrawPreviewLevelLabelExt(label_state);
2027   }
2028
2029   game_status = last_game_status;       /* restore current game status */
2030 }
2031
2032 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2033                                     int graphic, int sync_frame, int mask_mode)
2034 {
2035   int frame = getGraphicAnimationFrame(graphic, sync_frame);
2036
2037   if (mask_mode == USE_MASKING)
2038     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2039   else
2040     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2041 }
2042
2043 inline void DrawGraphicAnimation(int x, int y, int graphic)
2044 {
2045   int lx = LEVELX(x), ly = LEVELY(y);
2046
2047   if (!IN_SCR_FIELD(x, y))
2048     return;
2049
2050   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2051                           graphic, GfxFrame[lx][ly], NO_MASKING);
2052   MarkTileDirty(x, y);
2053 }
2054
2055 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2056 {
2057   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2058 }
2059
2060 void DrawLevelElementAnimation(int x, int y, int element)
2061 {
2062   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2063
2064   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2065 }
2066
2067 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2068 {
2069   int sx = SCREENX(x), sy = SCREENY(y);
2070
2071   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2072     return;
2073
2074   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2075     return;
2076
2077   DrawGraphicAnimation(sx, sy, graphic);
2078
2079 #if 1
2080   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2081     DrawLevelFieldCrumbledSand(x, y);
2082 #else
2083   if (GFX_CRUMBLED(Feld[x][y]))
2084     DrawLevelFieldCrumbledSand(x, y);
2085 #endif
2086 }
2087
2088 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2089 {
2090   int sx = SCREENX(x), sy = SCREENY(y);
2091   int graphic;
2092
2093   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2094     return;
2095
2096   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2097
2098   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2099     return;
2100
2101   DrawGraphicAnimation(sx, sy, graphic);
2102
2103   if (GFX_CRUMBLED(element))
2104     DrawLevelFieldCrumbledSand(x, y);
2105 }
2106
2107 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2108 {
2109   if (player->use_murphy)
2110   {
2111     /* this works only because currently only one player can be "murphy" ... */
2112     static int last_horizontal_dir = MV_LEFT;
2113     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2114
2115     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2116       last_horizontal_dir = move_dir;
2117
2118     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
2119     {
2120       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2121
2122       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2123     }
2124
2125     return graphic;
2126   }
2127   else
2128     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2129 }
2130
2131 static boolean equalGraphics(int graphic1, int graphic2)
2132 {
2133   struct GraphicInfo *g1 = &graphic_info[graphic1];
2134   struct GraphicInfo *g2 = &graphic_info[graphic2];
2135
2136   return (g1->bitmap      == g2->bitmap &&
2137           g1->src_x       == g2->src_x &&
2138           g1->src_y       == g2->src_y &&
2139           g1->anim_frames == g2->anim_frames &&
2140           g1->anim_delay  == g2->anim_delay &&
2141           g1->anim_mode   == g2->anim_mode);
2142 }
2143
2144 void DrawAllPlayers()
2145 {
2146   int i;
2147
2148   for (i = 0; i < MAX_PLAYERS; i++)
2149     if (stored_player[i].active)
2150       DrawPlayer(&stored_player[i]);
2151 }
2152
2153 void DrawPlayerField(int x, int y)
2154 {
2155   if (!IS_PLAYER(x, y))
2156     return;
2157
2158   DrawPlayer(PLAYERINFO(x, y));
2159 }
2160
2161 void DrawPlayer(struct PlayerInfo *player)
2162 {
2163   int jx = player->jx;
2164   int jy = player->jy;
2165   int move_dir = player->MovDir;
2166   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2167   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
2168   int last_jx = (player->is_moving ? jx - dx : jx);
2169   int last_jy = (player->is_moving ? jy - dy : jy);
2170   int next_jx = jx + dx;
2171   int next_jy = jy + dy;
2172   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
2173   boolean player_is_opaque = FALSE;
2174   int sx = SCREENX(jx), sy = SCREENY(jy);
2175   int sxx = 0, syy = 0;
2176   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
2177   int graphic;
2178   int action = ACTION_DEFAULT;
2179   int last_player_graphic = getPlayerGraphic(player, move_dir);
2180   int last_player_frame = player->Frame;
2181   int frame = 0;
2182
2183   /* GfxElement[][] is set to the element the player is digging or collecting;
2184      remove also for off-screen player if the player is not moving anymore */
2185   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
2186     GfxElement[jx][jy] = EL_UNDEFINED;
2187
2188   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
2189     return;
2190
2191 #if DEBUG
2192   if (!IN_LEV_FIELD(jx, jy))
2193   {
2194     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
2195     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
2196     printf("DrawPlayerField(): This should never happen!\n");
2197     return;
2198   }
2199 #endif
2200
2201   if (element == EL_EXPLOSION)
2202     return;
2203
2204   action = (player->is_pushing    ? ACTION_PUSHING         :
2205             player->is_digging    ? ACTION_DIGGING         :
2206             player->is_collecting ? ACTION_COLLECTING      :
2207             player->is_moving     ? ACTION_MOVING          :
2208             player->is_snapping   ? ACTION_SNAPPING        :
2209             player->is_dropping   ? ACTION_DROPPING        :
2210             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
2211
2212   if (player->is_waiting)
2213     move_dir = player->dir_waiting;
2214
2215   InitPlayerGfxAnimation(player, action, move_dir);
2216
2217   /* ----------------------------------------------------------------------- */
2218   /* draw things in the field the player is leaving, if needed               */
2219   /* ----------------------------------------------------------------------- */
2220
2221   if (player->is_moving)
2222   {
2223     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
2224     {
2225       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
2226
2227       if (last_element == EL_DYNAMITE_ACTIVE ||
2228           last_element == EL_EM_DYNAMITE_ACTIVE ||
2229           last_element == EL_SP_DISK_RED_ACTIVE)
2230         DrawDynamite(last_jx, last_jy);
2231       else
2232         DrawLevelFieldThruMask(last_jx, last_jy);
2233     }
2234     else if (last_element == EL_DYNAMITE_ACTIVE ||
2235              last_element == EL_EM_DYNAMITE_ACTIVE ||
2236              last_element == EL_SP_DISK_RED_ACTIVE)
2237       DrawDynamite(last_jx, last_jy);
2238 #if 0
2239     /* !!! this is not enough to prevent flickering of players which are
2240        moving next to each others without a free tile between them -- this
2241        can only be solved by drawing all players layer by layer (first the
2242        background, then the foreground etc.) !!! => TODO */
2243     else if (!IS_PLAYER(last_jx, last_jy))
2244       DrawLevelField(last_jx, last_jy);
2245 #else
2246     else
2247       DrawLevelField(last_jx, last_jy);
2248 #endif
2249
2250     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
2251       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
2252   }
2253
2254   if (!IN_SCR_FIELD(sx, sy))
2255     return;
2256
2257   if (setup.direct_draw)
2258     SetDrawtoField(DRAW_BUFFERED);
2259
2260   /* ----------------------------------------------------------------------- */
2261   /* draw things behind the player, if needed                                */
2262   /* ----------------------------------------------------------------------- */
2263
2264   if (Back[jx][jy])
2265     DrawLevelElement(jx, jy, Back[jx][jy]);
2266   else if (IS_ACTIVE_BOMB(element))
2267     DrawLevelElement(jx, jy, EL_EMPTY);
2268   else
2269   {
2270     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
2271     {
2272       int old_element = GfxElement[jx][jy];
2273       int old_graphic = el_act_dir2img(old_element, action, move_dir);
2274       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
2275
2276       if (GFX_CRUMBLED(old_element))
2277         DrawLevelFieldCrumbledSandDigging(jx, jy, move_dir, player->StepFrame);
2278       else
2279         DrawGraphic(sx, sy, old_graphic, frame);
2280
2281       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
2282         player_is_opaque = TRUE;
2283     }
2284     else
2285     {
2286       GfxElement[jx][jy] = EL_UNDEFINED;
2287
2288       /* make sure that pushed elements are drawn with correct frame rate */
2289 #if 1
2290       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
2291
2292       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
2293         GfxFrame[jx][jy] = player->StepFrame;
2294 #else
2295       if (player->is_pushing && player->is_moving)
2296         GfxFrame[jx][jy] = player->StepFrame;
2297 #endif
2298
2299       DrawLevelField(jx, jy);
2300     }
2301   }
2302
2303   /* ----------------------------------------------------------------------- */
2304   /* draw player himself                                                     */
2305   /* ----------------------------------------------------------------------- */
2306
2307   graphic = getPlayerGraphic(player, move_dir);
2308
2309   /* in the case of changed player action or direction, prevent the current
2310      animation frame from being restarted for identical animations */
2311   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
2312     player->Frame = last_player_frame;
2313
2314   frame = getGraphicAnimationFrame(graphic, player->Frame);
2315
2316   if (player->GfxPos)
2317   {
2318     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2319       sxx = player->GfxPos;
2320     else
2321       syy = player->GfxPos;
2322   }
2323
2324   if (!setup.soft_scrolling && ScreenMovPos)
2325     sxx = syy = 0;
2326
2327   if (player_is_opaque)
2328     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
2329   else
2330     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
2331
2332   if (SHIELD_ON(player))
2333   {
2334     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
2335                    IMG_SHIELD_NORMAL_ACTIVE);
2336     int frame = getGraphicAnimationFrame(graphic, -1);
2337
2338     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
2339   }
2340
2341   /* ----------------------------------------------------------------------- */
2342   /* draw things the player is pushing, if needed                            */
2343   /* ----------------------------------------------------------------------- */
2344
2345 #if 0
2346   printf("::: %d, %d [%d, %d] [%d]\n",
2347          player->is_pushing, player_is_moving, player->GfxAction,
2348          player->is_moving, player_is_moving);
2349 #endif
2350
2351 #if 1
2352   if (player->is_pushing && player->is_moving)
2353   {
2354     int px = SCREENX(jx), py = SCREENY(jy);
2355     int pxx = (TILEX - ABS(sxx)) * dx;
2356     int pyy = (TILEY - ABS(syy)) * dy;
2357     int gfx_frame = GfxFrame[jx][jy];
2358
2359     int graphic;
2360     int sync_frame;
2361     int frame;
2362
2363     if (!IS_MOVING(jx, jy))             /* push movement already finished */
2364     {
2365       element = Feld[next_jx][next_jy];
2366       gfx_frame = GfxFrame[next_jx][next_jy];
2367     }
2368
2369     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
2370
2371 #if 1
2372     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
2373     frame = getGraphicAnimationFrame(graphic, sync_frame);
2374 #else
2375     frame = getGraphicAnimationFrame(graphic, player->StepFrame);
2376 #endif
2377
2378     /* draw background element under pushed element (like the Sokoban field) */
2379     if (Back[next_jx][next_jy])
2380       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
2381
2382     /* masked drawing is needed for EMC style (double) movement graphics */
2383     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
2384   }
2385 #endif
2386
2387   /* ----------------------------------------------------------------------- */
2388   /* draw things in front of player (active dynamite or dynabombs)           */
2389   /* ----------------------------------------------------------------------- */
2390
2391   if (IS_ACTIVE_BOMB(element))
2392   {
2393     graphic = el2img(element);
2394     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
2395
2396     if (game.emulation == EMU_SUPAPLEX)
2397       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2398     else
2399       DrawGraphicThruMask(sx, sy, graphic, frame);
2400   }
2401
2402   if (player_is_moving && last_element == EL_EXPLOSION)
2403   {
2404     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
2405                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
2406     int graphic = el_act2img(element, ACTION_EXPLODING);
2407     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2408     int phase = ExplodePhase[last_jx][last_jy] - 1;
2409     int frame = getGraphicAnimationFrame(graphic, phase - delay);
2410
2411     if (phase >= delay)
2412       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
2413   }
2414
2415   /* ----------------------------------------------------------------------- */
2416   /* draw elements the player is just walking/passing through/under          */
2417   /* ----------------------------------------------------------------------- */
2418
2419   if (player_is_moving)
2420   {
2421     /* handle the field the player is leaving ... */
2422     if (IS_ACCESSIBLE_INSIDE(last_element))
2423       DrawLevelField(last_jx, last_jy);
2424     else if (IS_ACCESSIBLE_UNDER(last_element))
2425       DrawLevelFieldThruMask(last_jx, last_jy);
2426   }
2427
2428   /* do not redraw accessible elements if the player is just pushing them */
2429   if (!player_is_moving || !player->is_pushing)
2430   {
2431     /* ... and the field the player is entering */
2432     if (IS_ACCESSIBLE_INSIDE(element))
2433       DrawLevelField(jx, jy);
2434     else if (IS_ACCESSIBLE_UNDER(element))
2435       DrawLevelFieldThruMask(jx, jy);
2436   }
2437
2438   if (setup.direct_draw)
2439   {
2440     int dst_x = SX + SCREENX(MIN(jx, last_jx)) * TILEX;
2441     int dst_y = SY + SCREENY(MIN(jy, last_jy)) * TILEY;
2442     int x_size = TILEX * (1 + ABS(jx - last_jx));
2443     int y_size = TILEY * (1 + ABS(jy - last_jy));
2444
2445     BlitBitmap(drawto_field, window,
2446                dst_x, dst_y, x_size, y_size, dst_x, dst_y);
2447     SetDrawtoField(DRAW_DIRECT);
2448   }
2449
2450   MarkTileDirty(sx, sy);
2451 }
2452
2453 /* ------------------------------------------------------------------------- */
2454
2455 void WaitForEventToContinue()
2456 {
2457   boolean still_wait = TRUE;
2458
2459   /* simulate releasing mouse button over last gadget, if still pressed */
2460   if (button_status)
2461     HandleGadgets(-1, -1, 0);
2462
2463   button_status = MB_RELEASED;
2464
2465 #if 1
2466   ClearEventQueue();
2467 #endif
2468
2469   while (still_wait)
2470   {
2471     if (PendingEvent())
2472     {
2473       Event event;
2474
2475       NextEvent(&event);
2476
2477       switch (event.type)
2478       {
2479         case EVENT_BUTTONPRESS:
2480         case EVENT_KEYPRESS:
2481           still_wait = FALSE;
2482           break;
2483
2484         case EVENT_KEYRELEASE:
2485           ClearPlayerAction();
2486           break;
2487
2488         default:
2489           HandleOtherEvents(&event);
2490           break;
2491       }
2492     }
2493     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2494     {
2495       still_wait = FALSE;
2496     }
2497
2498     DoAnimation();
2499
2500     /* don't eat all CPU time */
2501     Delay(10);
2502   }
2503 }
2504
2505 #define MAX_REQUEST_LINES               13
2506 #define MAX_REQUEST_LINE_FONT1_LEN      7
2507 #define MAX_REQUEST_LINE_FONT2_LEN      10
2508
2509 boolean Request(char *text, unsigned int req_state)
2510 {
2511   int mx, my, ty, result = -1;
2512   unsigned int old_door_state;
2513   int last_game_status = game_status;   /* save current game status */
2514   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
2515   int font_nr = FONT_TEXT_2;
2516   int max_word_len = 0;
2517   char *text_ptr;
2518
2519   for (text_ptr = text; *text_ptr; text_ptr++)
2520   {
2521     max_word_len = (*text_ptr != ' ' ? max_word_len + 1 : 0);
2522
2523     if (max_word_len > MAX_REQUEST_LINE_FONT1_LEN)
2524     {
2525       max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
2526 #if 1
2527       font_nr = FONT_TEXT_1;
2528 #else
2529       font_nr = FONT_LEVEL_NUMBER;
2530 #endif
2531
2532       break;
2533     }
2534   }
2535
2536   if (game_status == GAME_MODE_PLAYING &&
2537       level.game_engine_type == GAME_ENGINE_TYPE_EM)
2538     BlitScreenToBitmap_EM(backbuffer);
2539
2540   /* disable deactivated drawing when quick-loading level tape recording */
2541   if (tape.playing && tape.deactivate_display)
2542     TapeDeactivateDisplayOff(TRUE);
2543
2544   SetMouseCursor(CURSOR_DEFAULT);
2545
2546 #if defined(NETWORK_AVALIABLE)
2547   /* pause network game while waiting for request to answer */
2548   if (options.network &&
2549       game_status == GAME_MODE_PLAYING &&
2550       req_state & REQUEST_WAIT_FOR_INPUT)
2551     SendToServer_PausePlaying();
2552 #endif
2553
2554   old_door_state = GetDoorState();
2555
2556   /* simulate releasing mouse button over last gadget, if still pressed */
2557   if (button_status)
2558     HandleGadgets(-1, -1, 0);
2559
2560   UnmapAllGadgets();
2561
2562   if (old_door_state & DOOR_OPEN_1)
2563   {
2564     CloseDoor(DOOR_CLOSE_1);
2565
2566     /* save old door content */
2567     BlitBitmap(bitmap_db_door, bitmap_db_door,
2568                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2569                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
2570   }
2571
2572 #if 1
2573   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
2574 #endif
2575
2576   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2577
2578   /* clear door drawing field */
2579   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2580
2581   /* force DOOR font on preview level */
2582   game_status = GAME_MODE_PSEUDO_DOOR;
2583
2584   /* write text for request */
2585   for (ty = 0; ty < MAX_REQUEST_LINES; ty++)
2586   {
2587     char text_line[max_request_line_len + 1];
2588     int tx, tl, tc = 0;
2589
2590     if (!*text)
2591       break;
2592
2593     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
2594     {
2595       tc = *(text + tx);
2596       if (!tc || tc == ' ')
2597         break;
2598     }
2599
2600     if (!tl)
2601     { 
2602       text++; 
2603       ty--; 
2604       continue; 
2605     }
2606
2607     strncpy(text_line, text, tl);
2608     text_line[tl] = 0;
2609
2610     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
2611              DY + 8 + ty * (getFontHeight(font_nr) + 2),
2612              text_line, font_nr);
2613
2614     text += tl + (tc == ' ' ? 1 : 0);
2615   }
2616
2617   game_status = last_game_status;       /* restore current game status */
2618
2619   if (req_state & REQ_ASK)
2620   {
2621     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2622     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2623   }
2624   else if (req_state & REQ_CONFIRM)
2625   {
2626     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2627   }
2628   else if (req_state & REQ_PLAYER)
2629   {
2630     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2631     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2632     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2633     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2634   }
2635
2636   /* copy request gadgets to door backbuffer */
2637   BlitBitmap(drawto, bitmap_db_door,
2638              DX, DY, DXSIZE, DYSIZE,
2639              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2640
2641   OpenDoor(DOOR_OPEN_1);
2642
2643   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
2644   {
2645     if (game_status == GAME_MODE_PLAYING)
2646     {
2647       SetPanelBackground();
2648       SetDrawBackgroundMask(REDRAW_DOOR_1);
2649     }
2650     else
2651     {
2652       SetDrawBackgroundMask(REDRAW_FIELD);
2653     }
2654
2655     return FALSE;
2656   }
2657
2658   if (game_status != GAME_MODE_MAIN)
2659     InitAnimation();
2660
2661   button_status = MB_RELEASED;
2662
2663   request_gadget_id = -1;
2664
2665   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2666
2667   while (result < 0)
2668   {
2669     if (PendingEvent())
2670     {
2671       Event event;
2672
2673       NextEvent(&event);
2674
2675       switch (event.type)
2676       {
2677         case EVENT_BUTTONPRESS:
2678         case EVENT_BUTTONRELEASE:
2679         case EVENT_MOTIONNOTIFY:
2680         {
2681           if (event.type == EVENT_MOTIONNOTIFY)
2682           {
2683             if (!PointerInWindow(window))
2684               continue; /* window and pointer are on different screens */
2685
2686             if (!button_status)
2687               continue;
2688
2689             motion_status = TRUE;
2690             mx = ((MotionEvent *) &event)->x;
2691             my = ((MotionEvent *) &event)->y;
2692           }
2693           else
2694           {
2695             motion_status = FALSE;
2696             mx = ((ButtonEvent *) &event)->x;
2697             my = ((ButtonEvent *) &event)->y;
2698             if (event.type == EVENT_BUTTONPRESS)
2699               button_status = ((ButtonEvent *) &event)->button;
2700             else
2701               button_status = MB_RELEASED;
2702           }
2703
2704           /* this sets 'request_gadget_id' */
2705           HandleGadgets(mx, my, button_status);
2706
2707           switch (request_gadget_id)
2708           {
2709             case TOOL_CTRL_ID_YES:
2710               result = TRUE;
2711               break;
2712             case TOOL_CTRL_ID_NO:
2713               result = FALSE;
2714               break;
2715             case TOOL_CTRL_ID_CONFIRM:
2716               result = TRUE | FALSE;
2717               break;
2718
2719             case TOOL_CTRL_ID_PLAYER_1:
2720               result = 1;
2721               break;
2722             case TOOL_CTRL_ID_PLAYER_2:
2723               result = 2;
2724               break;
2725             case TOOL_CTRL_ID_PLAYER_3:
2726               result = 3;
2727               break;
2728             case TOOL_CTRL_ID_PLAYER_4:
2729               result = 4;
2730               break;
2731
2732             default:
2733               break;
2734           }
2735
2736           break;
2737         }
2738
2739         case EVENT_KEYPRESS:
2740           switch (GetEventKey((KeyEvent *)&event, TRUE))
2741           {
2742             case KSYM_Return:
2743               result = 1;
2744               break;
2745
2746             case KSYM_Escape:
2747               result = 0;
2748               break;
2749
2750             default:
2751               break;
2752           }
2753           if (req_state & REQ_PLAYER)
2754             result = 0;
2755           break;
2756
2757         case EVENT_KEYRELEASE:
2758           ClearPlayerAction();
2759           break;
2760
2761         default:
2762           HandleOtherEvents(&event);
2763           break;
2764       }
2765     }
2766     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2767     {
2768       int joy = AnyJoystick();
2769
2770       if (joy & JOY_BUTTON_1)
2771         result = 1;
2772       else if (joy & JOY_BUTTON_2)
2773         result = 0;
2774     }
2775
2776     DoAnimation();
2777
2778 #if 1
2779     if (!PendingEvent())        /* delay only if no pending events */
2780       Delay(10);
2781 #else
2782     /* don't eat all CPU time */
2783     Delay(10);
2784 #endif
2785   }
2786
2787   if (game_status != GAME_MODE_MAIN)
2788     StopAnimation();
2789
2790   UnmapToolButtons();
2791
2792   if (!(req_state & REQ_STAY_OPEN))
2793   {
2794     CloseDoor(DOOR_CLOSE_1);
2795
2796     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
2797         (req_state & REQ_REOPEN))
2798       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
2799   }
2800
2801   RemapAllGadgets();
2802
2803   if (game_status == GAME_MODE_PLAYING)
2804   {
2805     SetPanelBackground();
2806     SetDrawBackgroundMask(REDRAW_DOOR_1);
2807   }
2808   else
2809   {
2810     SetDrawBackgroundMask(REDRAW_FIELD);
2811   }
2812
2813 #if defined(NETWORK_AVALIABLE)
2814   /* continue network game after request */
2815   if (options.network &&
2816       game_status == GAME_MODE_PLAYING &&
2817       req_state & REQUEST_WAIT_FOR_INPUT)
2818     SendToServer_ContinuePlaying();
2819 #endif
2820
2821   /* restore deactivated drawing when quick-loading level tape recording */
2822   if (tape.playing && tape.deactivate_display)
2823     TapeDeactivateDisplayOn();
2824
2825   return result;
2826 }
2827
2828 unsigned int OpenDoor(unsigned int door_state)
2829 {
2830   if (door_state & DOOR_COPY_BACK)
2831   {
2832     if (door_state & DOOR_OPEN_1)
2833       BlitBitmap(bitmap_db_door, bitmap_db_door,
2834                  DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2835                  DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2836
2837     if (door_state & DOOR_OPEN_2)
2838       BlitBitmap(bitmap_db_door, bitmap_db_door,
2839                  DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY2, VXSIZE, VYSIZE,
2840                  DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2841
2842     door_state &= ~DOOR_COPY_BACK;
2843   }
2844
2845   return MoveDoor(door_state);
2846 }
2847
2848 unsigned int CloseDoor(unsigned int door_state)
2849 {
2850   unsigned int old_door_state = GetDoorState();
2851
2852   if (!(door_state & DOOR_NO_COPY_BACK))
2853   {
2854     if (old_door_state & DOOR_OPEN_1)
2855       BlitBitmap(backbuffer, bitmap_db_door,
2856                  DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2857
2858     if (old_door_state & DOOR_OPEN_2)
2859       BlitBitmap(backbuffer, bitmap_db_door,
2860                  VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2861
2862     door_state &= ~DOOR_NO_COPY_BACK;
2863   }
2864
2865   return MoveDoor(door_state);
2866 }
2867
2868 unsigned int GetDoorState()
2869 {
2870   return MoveDoor(DOOR_GET_STATE);
2871 }
2872
2873 unsigned int SetDoorState(unsigned int door_state)
2874 {
2875   return MoveDoor(door_state | DOOR_SET_STATE);
2876 }
2877
2878 unsigned int MoveDoor(unsigned int door_state)
2879 {
2880   static int door1 = DOOR_OPEN_1;
2881   static int door2 = DOOR_CLOSE_2;
2882   unsigned long door_delay = 0;
2883   unsigned long door_delay_value;
2884   int stepsize = 1;
2885
2886   if (door_1.width < 0 || door_1.width > DXSIZE)
2887     door_1.width = DXSIZE;
2888   if (door_1.height < 0 || door_1.height > DYSIZE)
2889     door_1.height = DYSIZE;
2890   if (door_2.width < 0 || door_2.width > VXSIZE)
2891     door_2.width = VXSIZE;
2892   if (door_2.height < 0 || door_2.height > VYSIZE)
2893     door_2.height = VYSIZE;
2894
2895   if (door_state == DOOR_GET_STATE)
2896     return (door1 | door2);
2897
2898   if (door_state & DOOR_SET_STATE)
2899   {
2900     if (door_state & DOOR_ACTION_1)
2901       door1 = door_state & DOOR_ACTION_1;
2902     if (door_state & DOOR_ACTION_2)
2903       door2 = door_state & DOOR_ACTION_2;
2904
2905     return (door1 | door2);
2906   }
2907
2908   if (!(door_state & DOOR_FORCE_REDRAW))
2909   {
2910     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2911       door_state &= ~DOOR_OPEN_1;
2912     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2913       door_state &= ~DOOR_CLOSE_1;
2914     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2915       door_state &= ~DOOR_OPEN_2;
2916     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2917       door_state &= ~DOOR_CLOSE_2;
2918   }
2919
2920   door_delay_value = (door_state & DOOR_ACTION_1 ? door_1.step_delay :
2921                       door_2.step_delay);
2922
2923   if (setup.quick_doors)
2924   {
2925     stepsize = 20;              /* must be choosen to always draw last frame */
2926     door_delay_value = 0;
2927   }
2928
2929   if (global.autoplay_leveldir)
2930   {
2931     door_state |= DOOR_NO_DELAY;
2932     door_state &= ~DOOR_CLOSE_ALL;
2933   }
2934
2935   if (door_state & DOOR_ACTION)
2936   {
2937     boolean handle_door_1 = (door_state & DOOR_ACTION_1);
2938     boolean handle_door_2 = (door_state & DOOR_ACTION_2);
2939     boolean door_1_done = (!handle_door_1);
2940     boolean door_2_done = (!handle_door_2);
2941     boolean door_1_vertical = (door_1.anim_mode & ANIM_VERTICAL);
2942     boolean door_2_vertical = (door_2.anim_mode & ANIM_VERTICAL);
2943     int door_size_1 = (door_1_vertical ? door_1.height : door_1.width);
2944     int door_size_2 = (door_2_vertical ? door_2.height : door_2.width);
2945     int max_door_size_1 = (door_1_vertical ? DYSIZE : DXSIZE);
2946     int max_door_size_2 = (door_2_vertical ? VYSIZE : VXSIZE);
2947     int door_size     = (handle_door_1 ? door_size_1     : door_size_2);
2948     int max_door_size = (handle_door_1 ? max_door_size_1 : max_door_size_2);
2949     int door_skip = max_door_size - door_size;
2950     int end = door_size;
2951     int start = ((door_state & DOOR_NO_DELAY) ? end : 0);
2952     int k;
2953
2954     if (!(door_state & DOOR_NO_DELAY) && !setup.quick_doors)
2955     {
2956       /* opening door sound has priority over simultaneously closing door */
2957       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2958         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2959       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2960         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2961     }
2962
2963     for (k = start; k <= end && !(door_1_done && door_2_done); k += stepsize)
2964     {
2965       int x = k;
2966       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2967       GC gc = bitmap->stored_clip_gc;
2968
2969       if (door_state & DOOR_ACTION_1)
2970       {
2971         int a = MIN(x * door_1.step_offset, end);
2972         int p = (door_state & DOOR_OPEN_1 ? end - a : a);
2973         int i = p + door_skip;
2974
2975         if (door_1.anim_mode & ANIM_STATIC_PANEL)
2976         {
2977           BlitBitmap(bitmap_db_door, drawto,
2978                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1,
2979                      DXSIZE, DYSIZE, DX, DY);
2980         }
2981         else if (x <= a)
2982         {
2983           BlitBitmap(bitmap_db_door, drawto,
2984                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + p / 2,
2985                      DXSIZE, DYSIZE - p / 2, DX, DY);
2986
2987           ClearRectangle(drawto, DX, DY + DYSIZE - p / 2, DXSIZE, p / 2);
2988         }
2989
2990         if (door_1.anim_mode & ANIM_HORIZONTAL && x <= DXSIZE)
2991         {
2992           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2993           int dst1_x = DX + DXSIZE - i, dst1_y = DY;
2994           int src2_x = DXSIZE - i,      src2_y = DOOR_GFX_PAGEY1;
2995           int dst2_x = DX,              dst2_y = DY;
2996           int width = i, height = DYSIZE;
2997
2998           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2999           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
3000                            dst1_x, dst1_y);
3001
3002           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
3003           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
3004                            dst2_x, dst2_y);
3005         }
3006         else if (door_1.anim_mode & ANIM_VERTICAL && x <= DYSIZE)
3007         {
3008           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
3009           int dst1_x = DX,              dst1_y = DY + DYSIZE - i;
3010           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY1 + DYSIZE - i;
3011           int dst2_x = DX,              dst2_y = DY;
3012           int width = DXSIZE, height = i;
3013
3014           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
3015           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
3016                            dst1_x, dst1_y);
3017
3018           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
3019           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
3020                            dst2_x, dst2_y);
3021         }
3022         else if (x <= DXSIZE)   /* ANIM_DEFAULT */
3023         {
3024           int j = (door_1.anim_mode == ANIM_DEFAULT ? (DXSIZE - i) / 3 : 0);
3025
3026           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
3027           BlitBitmapMasked(bitmap, drawto,
3028                            DXSIZE, DOOR_GFX_PAGEY1, i, 77,
3029                            DX + DXSIZE - i, DY + j);
3030           BlitBitmapMasked(bitmap, drawto,
3031                            DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
3032                            DX + DXSIZE - i, DY + 140 + j);
3033           SetClipOrigin(bitmap, gc, DX - DXSIZE + i,
3034                         DY - (DOOR_GFX_PAGEY1 + j));
3035           BlitBitmapMasked(bitmap, drawto,
3036                            DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
3037                            DX, DY);
3038           BlitBitmapMasked(bitmap, drawto,
3039                            DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
3040                            DX, DY + 140 - j);
3041
3042           BlitBitmapMasked(bitmap, drawto,
3043                            DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
3044                            DX, DY + 77 - j);
3045           BlitBitmapMasked(bitmap, drawto,
3046                            DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
3047                            DX, DY + 203 - j);
3048           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
3049           BlitBitmapMasked(bitmap, drawto,
3050                            DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
3051                            DX + DXSIZE - i, DY + 77 + j);
3052           BlitBitmapMasked(bitmap, drawto,
3053                            DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
3054                            DX + DXSIZE - i, DY + 203 + j);
3055         }
3056
3057         redraw_mask |= REDRAW_DOOR_1;
3058         door_1_done = (a == end);
3059       }
3060
3061       if (door_state & DOOR_ACTION_2)
3062       {
3063         int a = MIN(x * door_2.step_offset, door_size);
3064         int p = (door_state & DOOR_OPEN_2 ? door_size - a : a);
3065         int i = p + door_skip;
3066
3067         if (door_2.anim_mode & ANIM_STATIC_PANEL)
3068         {
3069           BlitBitmap(bitmap_db_door, drawto,
3070                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2,
3071                      VXSIZE, VYSIZE, VX, VY);
3072         }
3073         else if (x <= VYSIZE)
3074         {
3075           BlitBitmap(bitmap_db_door, drawto,
3076                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + p / 2,
3077                      VXSIZE, VYSIZE - p / 2, VX, VY);
3078
3079           ClearRectangle(drawto, VX, VY + VYSIZE - p / 2, VXSIZE, p / 2);
3080         }
3081
3082         if (door_2.anim_mode & ANIM_HORIZONTAL && x <= VXSIZE)
3083         {
3084           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
3085           int dst1_x = VX + VXSIZE - i, dst1_y = VY;
3086           int src2_x = VXSIZE - i,      src2_y = DOOR_GFX_PAGEY2;
3087           int dst2_x = VX,              dst2_y = VY;
3088           int width = i, height = VYSIZE;
3089
3090           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
3091           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
3092                            dst1_x, dst1_y);
3093
3094           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
3095           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
3096                            dst2_x, dst2_y);
3097         }
3098         else if (door_2.anim_mode & ANIM_VERTICAL && x <= VYSIZE)
3099         {
3100           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
3101           int dst1_x = VX,              dst1_y = VY + VYSIZE - i;
3102           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY2 + VYSIZE - i;
3103           int dst2_x = VX,              dst2_y = VY;
3104           int width = VXSIZE, height = i;
3105
3106           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
3107           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
3108                            dst1_x, dst1_y);
3109
3110           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
3111           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
3112                            dst2_x, dst2_y);
3113         }
3114         else if (x <= VXSIZE)   /* ANIM_DEFAULT */
3115         {
3116           int j = (door_2.anim_mode == ANIM_DEFAULT ? (VXSIZE - i) / 3 : 0);
3117
3118           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
3119           BlitBitmapMasked(bitmap, drawto,
3120                            VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
3121                            VX + VXSIZE - i, VY + j);
3122           SetClipOrigin(bitmap, gc,
3123                         VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
3124           BlitBitmapMasked(bitmap, drawto,
3125                            VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
3126                            VX, VY);
3127
3128           BlitBitmapMasked(bitmap, drawto,
3129                            VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
3130                            i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
3131           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
3132           BlitBitmapMasked(bitmap, drawto,
3133                            VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
3134                            i, VYSIZE / 2 - j,
3135                            VX + VXSIZE - i, VY + VYSIZE / 2 + j);
3136         }
3137
3138         redraw_mask |= REDRAW_DOOR_2;
3139         door_2_done = (a == VXSIZE);
3140       }
3141
3142       if (!(door_state & DOOR_NO_DELAY))
3143       {
3144         BackToFront();
3145
3146         if (game_status == GAME_MODE_MAIN)
3147           DoAnimation();
3148
3149         WaitUntilDelayReached(&door_delay, door_delay_value);
3150       }
3151     }
3152   }
3153
3154   if (door_state & DOOR_ACTION_1)
3155     door1 = door_state & DOOR_ACTION_1;
3156   if (door_state & DOOR_ACTION_2)
3157     door2 = door_state & DOOR_ACTION_2;
3158
3159   return (door1 | door2);
3160 }
3161
3162 void DrawSpecialEditorDoor()
3163 {
3164   /* draw bigger toolbox window */
3165   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3166              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
3167              EX - 4, EY - 12);
3168   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
3169              EX - 6, VY - 4, EXSIZE + 12, EYSIZE - VYSIZE + 4,
3170              EX - 6, EY - 4);
3171
3172   redraw_mask |= REDRAW_ALL;
3173 }
3174
3175 void UndrawSpecialEditorDoor()
3176 {
3177   /* draw normal tape recorder window */
3178   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
3179              EX - 6, EY - 12, EXSIZE + 12, EYSIZE - VYSIZE + 12,
3180              EX - 6, EY - 12);
3181
3182   redraw_mask |= REDRAW_ALL;
3183 }
3184
3185
3186 /* ---------- new tool button stuff ---------------------------------------- */
3187
3188 /* graphic position values for tool buttons */
3189 #define TOOL_BUTTON_YES_XPOS            2
3190 #define TOOL_BUTTON_YES_YPOS            250
3191 #define TOOL_BUTTON_YES_GFX_YPOS        0
3192 #define TOOL_BUTTON_YES_XSIZE           46
3193 #define TOOL_BUTTON_YES_YSIZE           28
3194 #define TOOL_BUTTON_NO_XPOS             52
3195 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
3196 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
3197 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
3198 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
3199 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
3200 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
3201 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
3202 #define TOOL_BUTTON_CONFIRM_XSIZE       96
3203 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
3204 #define TOOL_BUTTON_PLAYER_XSIZE        30
3205 #define TOOL_BUTTON_PLAYER_YSIZE        30
3206 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
3207 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
3208 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
3209 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
3210 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
3211                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
3212 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
3213                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
3214 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
3215                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
3216 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
3217                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
3218 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
3219                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
3220 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
3221                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
3222 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
3223                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
3224 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
3225                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
3226
3227 static struct
3228 {
3229   int xpos, ypos;
3230   int x, y;
3231   int width, height;
3232   int gadget_id;
3233   char *infotext;
3234 } toolbutton_info[NUM_TOOL_BUTTONS] =
3235 {
3236   {
3237     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
3238     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
3239     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
3240     TOOL_CTRL_ID_YES,
3241     "yes"
3242   },
3243   {
3244     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
3245     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
3246     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
3247     TOOL_CTRL_ID_NO,
3248     "no"
3249   },
3250   {
3251     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
3252     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
3253     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
3254     TOOL_CTRL_ID_CONFIRM,
3255     "confirm"
3256   },
3257   {
3258     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
3259     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
3260     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
3261     TOOL_CTRL_ID_PLAYER_1,
3262     "player 1"
3263   },
3264   {
3265     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
3266     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
3267     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
3268     TOOL_CTRL_ID_PLAYER_2,
3269     "player 2"
3270   },
3271   {
3272     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
3273     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
3274     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
3275     TOOL_CTRL_ID_PLAYER_3,
3276     "player 3"
3277   },
3278   {
3279     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
3280     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
3281     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
3282     TOOL_CTRL_ID_PLAYER_4,
3283     "player 4"
3284   }
3285 };
3286
3287 void CreateToolButtons()
3288 {
3289   int i;
3290
3291   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3292   {
3293     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
3294     Bitmap *deco_bitmap = None;
3295     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
3296     struct GadgetInfo *gi;
3297     unsigned long event_mask;
3298     int gd_xoffset, gd_yoffset;
3299     int gd_x1, gd_x2, gd_y;
3300     int id = i;
3301
3302     event_mask = GD_EVENT_RELEASED;
3303
3304     gd_xoffset = toolbutton_info[i].xpos;
3305     gd_yoffset = toolbutton_info[i].ypos;
3306     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
3307     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
3308     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
3309
3310     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
3311     {
3312       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
3313
3314       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
3315                            &deco_bitmap, &deco_x, &deco_y);
3316       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
3317       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
3318     }
3319
3320     gi = CreateGadget(GDI_CUSTOM_ID, id,
3321                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
3322                       GDI_X, DX + toolbutton_info[i].x,
3323                       GDI_Y, DY + toolbutton_info[i].y,
3324                       GDI_WIDTH, toolbutton_info[i].width,
3325                       GDI_HEIGHT, toolbutton_info[i].height,
3326                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
3327                       GDI_STATE, GD_BUTTON_UNPRESSED,
3328                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
3329                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
3330                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
3331                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
3332                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
3333                       GDI_DECORATION_SHIFTING, 1, 1,
3334                       GDI_DIRECT_DRAW, FALSE,
3335                       GDI_EVENT_MASK, event_mask,
3336                       GDI_CALLBACK_ACTION, HandleToolButtons,
3337                       GDI_END);
3338
3339     if (gi == NULL)
3340       Error(ERR_EXIT, "cannot create gadget");
3341
3342     tool_gadget[id] = gi;
3343   }
3344 }
3345
3346 void FreeToolButtons()
3347 {
3348   int i;
3349
3350   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3351     FreeGadget(tool_gadget[i]);
3352 }
3353
3354 static void UnmapToolButtons()
3355 {
3356   int i;
3357
3358   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3359     UnmapGadget(tool_gadget[i]);
3360 }
3361
3362 static void HandleToolButtons(struct GadgetInfo *gi)
3363 {
3364   request_gadget_id = gi->custom_id;
3365 }
3366
3367 static struct Mapping_EM_to_RND_object
3368 {
3369   int element_em;
3370   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
3371   boolean is_backside;                  /* backside of moving element */
3372
3373   int element_rnd;
3374   int action;
3375   int direction;
3376 }
3377 em_object_mapping_list[] =
3378 {
3379   {
3380     Xblank,                             TRUE,   FALSE,
3381     EL_EMPTY,                           -1, -1
3382   },
3383   {
3384     Yacid_splash_eB,                    FALSE,  FALSE,
3385     EL_ACID_SPLASH_RIGHT,               -1, -1
3386   },
3387   {
3388     Yacid_splash_wB,                    FALSE,  FALSE,
3389     EL_ACID_SPLASH_LEFT,                -1, -1
3390   },
3391
3392 #ifdef EM_ENGINE_BAD_ROLL
3393   {
3394     Xstone_force_e,                     FALSE,  FALSE,
3395     EL_ROCK,                            -1, MV_BIT_RIGHT
3396   },
3397   {
3398     Xstone_force_w,                     FALSE,  FALSE,
3399     EL_ROCK,                            -1, MV_BIT_LEFT
3400   },
3401   {
3402     Xnut_force_e,                       FALSE,  FALSE,
3403     EL_NUT,                             -1, MV_BIT_RIGHT
3404   },
3405   {
3406     Xnut_force_w,                       FALSE,  FALSE,
3407     EL_NUT,                             -1, MV_BIT_LEFT
3408   },
3409   {
3410     Xspring_force_e,                    FALSE,  FALSE,
3411     EL_SPRING,                          -1, MV_BIT_RIGHT
3412   },
3413   {
3414     Xspring_force_w,                    FALSE,  FALSE,
3415     EL_SPRING,                          -1, MV_BIT_LEFT
3416   },
3417   {
3418     Xemerald_force_e,                   FALSE,  FALSE,
3419     EL_EMERALD,                         -1, MV_BIT_RIGHT
3420   },
3421   {
3422     Xemerald_force_w,                   FALSE,  FALSE,
3423     EL_EMERALD,                         -1, MV_BIT_LEFT
3424   },
3425   {
3426     Xdiamond_force_e,                   FALSE,  FALSE,
3427     EL_DIAMOND,                         -1, MV_BIT_RIGHT
3428   },
3429   {
3430     Xdiamond_force_w,                   FALSE,  FALSE,
3431     EL_DIAMOND,                         -1, MV_BIT_LEFT
3432   },
3433   {
3434     Xbomb_force_e,                      FALSE,  FALSE,
3435     EL_BOMB,                            -1, MV_BIT_RIGHT
3436   },
3437   {
3438     Xbomb_force_w,                      FALSE,  FALSE,
3439     EL_BOMB,                            -1, MV_BIT_LEFT
3440   },
3441 #endif  /* EM_ENGINE_BAD_ROLL */
3442
3443   {
3444     Xstone,                             TRUE,   FALSE,
3445     EL_ROCK,                            -1, -1
3446   },
3447   {
3448     Xstone_pause,                       FALSE,  FALSE,
3449     EL_ROCK,                            -1, -1
3450   },
3451   {
3452     Xstone_fall,                        FALSE,  FALSE,
3453     EL_ROCK,                            -1, -1
3454   },
3455   {
3456     Ystone_s,                           FALSE,  FALSE,
3457     EL_ROCK,                            ACTION_FALLING, -1
3458   },
3459   {
3460     Ystone_sB,                          FALSE,  TRUE,
3461     EL_ROCK,                            ACTION_FALLING, -1
3462   },
3463   {
3464     Ystone_e,                           FALSE,  FALSE,
3465     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
3466   },
3467   {
3468     Ystone_eB,                          FALSE,  TRUE,
3469     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
3470   },
3471   {
3472     Ystone_w,                           FALSE,  FALSE,
3473     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
3474   },
3475   {
3476     Ystone_wB,                          FALSE,  TRUE,
3477     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
3478   },
3479   {
3480     Xnut,                               TRUE,   FALSE,
3481     EL_NUT,                             -1, -1
3482   },
3483   {
3484     Xnut_pause,                         FALSE,  FALSE,
3485     EL_NUT,                             -1, -1
3486   },
3487   {
3488     Xnut_fall,                          FALSE,  FALSE,
3489     EL_NUT,                             -1, -1
3490   },
3491   {
3492     Ynut_s,                             FALSE,  FALSE,
3493     EL_NUT,                             ACTION_FALLING, -1
3494   },
3495   {
3496     Ynut_sB,                            FALSE,  TRUE,
3497     EL_NUT,                             ACTION_FALLING, -1
3498   },
3499   {
3500     Ynut_e,                             FALSE,  FALSE,
3501     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
3502   },
3503   {
3504     Ynut_eB,                            FALSE,  TRUE,
3505     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
3506   },
3507   {
3508     Ynut_w,                             FALSE,  FALSE,
3509     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
3510   },
3511   {
3512     Ynut_wB,                            FALSE,  TRUE,
3513     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
3514   },
3515   {
3516     Xbug_n,                             TRUE,   FALSE,
3517     EL_BUG_UP,                          -1, -1
3518   },
3519   {
3520     Xbug_e,                             TRUE,   FALSE,
3521     EL_BUG_RIGHT,                       -1, -1
3522   },
3523   {
3524     Xbug_s,                             TRUE,   FALSE,
3525     EL_BUG_DOWN,                        -1, -1
3526   },
3527   {
3528     Xbug_w,                             TRUE,   FALSE,
3529     EL_BUG_LEFT,                        -1, -1
3530   },
3531   {
3532     Xbug_gon,                           FALSE,  FALSE,
3533     EL_BUG_UP,                          -1, -1
3534   },
3535   {
3536     Xbug_goe,                           FALSE,  FALSE,
3537     EL_BUG_RIGHT,                       -1, -1
3538   },
3539   {
3540     Xbug_gos,                           FALSE,  FALSE,
3541     EL_BUG_DOWN,                        -1, -1
3542   },
3543   {
3544     Xbug_gow,                           FALSE,  FALSE,
3545     EL_BUG_LEFT,                        -1, -1
3546   },
3547   {
3548     Ybug_n,                             FALSE,  FALSE,
3549     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
3550   },
3551   {
3552     Ybug_nB,                            FALSE,  TRUE,
3553     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
3554   },
3555   {
3556     Ybug_e,                             FALSE,  FALSE,
3557     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
3558   },
3559   {
3560     Ybug_eB,                            FALSE,  TRUE,
3561     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
3562   },
3563   {
3564     Ybug_s,                             FALSE,  FALSE,
3565     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
3566   },
3567   {
3568     Ybug_sB,                            FALSE,  TRUE,
3569     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
3570   },
3571   {
3572     Ybug_w,                             FALSE,  FALSE,
3573     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
3574   },
3575   {
3576     Ybug_wB,                            FALSE,  TRUE,
3577     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
3578   },
3579   {
3580     Ybug_w_n,                           FALSE,  FALSE,
3581     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
3582   },
3583   {
3584     Ybug_n_e,                           FALSE,  FALSE,
3585     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
3586   },
3587   {
3588     Ybug_e_s,                           FALSE,  FALSE,
3589     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
3590   },
3591   {
3592     Ybug_s_w,                           FALSE,  FALSE,
3593     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
3594   },
3595   {
3596     Ybug_e_n,                           FALSE,  FALSE,
3597     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
3598   },
3599   {
3600     Ybug_s_e,                           FALSE,  FALSE,
3601     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
3602   },
3603   {
3604     Ybug_w_s,                           FALSE,  FALSE,
3605     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
3606   },
3607   {
3608     Ybug_n_w,                           FALSE,  FALSE,
3609     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
3610   },
3611   {
3612     Ybug_stone,                         FALSE,  FALSE,
3613     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
3614   },
3615   {
3616     Ybug_spring,                        FALSE,  FALSE,
3617     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
3618   },
3619   {
3620     Xtank_n,                            TRUE,   FALSE,
3621     EL_SPACESHIP_UP,                    -1, -1
3622   },
3623   {
3624     Xtank_e,                            TRUE,   FALSE,
3625     EL_SPACESHIP_RIGHT,                 -1, -1
3626   },
3627   {
3628     Xtank_s,                            TRUE,   FALSE,
3629     EL_SPACESHIP_DOWN,                  -1, -1
3630   },
3631   {
3632     Xtank_w,                            TRUE,   FALSE,
3633     EL_SPACESHIP_LEFT,                  -1, -1
3634   },
3635   {
3636     Xtank_gon,                          FALSE,  FALSE,
3637     EL_SPACESHIP_UP,                    -1, -1
3638   },
3639   {
3640     Xtank_goe,                          FALSE,  FALSE,
3641     EL_SPACESHIP_RIGHT,                 -1, -1
3642   },
3643   {
3644     Xtank_gos,                          FALSE,  FALSE,
3645     EL_SPACESHIP_DOWN,                  -1, -1
3646   },
3647   {
3648     Xtank_gow,                          FALSE,  FALSE,
3649     EL_SPACESHIP_LEFT,                  -1, -1
3650   },
3651   {
3652     Ytank_n,                            FALSE,  FALSE,
3653     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
3654   },
3655   {
3656     Ytank_nB,                           FALSE,  TRUE,
3657     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
3658   },
3659   {
3660     Ytank_e,                            FALSE,  FALSE,
3661     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
3662   },
3663   {
3664     Ytank_eB,                           FALSE,  TRUE,
3665     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
3666   },
3667   {
3668     Ytank_s,                            FALSE,  FALSE,
3669     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
3670   },
3671   {
3672     Ytank_sB,                           FALSE,  TRUE,
3673     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
3674   },
3675   {
3676     Ytank_w,                            FALSE,  FALSE,
3677     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
3678   },
3679   {
3680     Ytank_wB,                           FALSE,  TRUE,
3681     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
3682   },
3683   {
3684     Ytank_w_n,                          FALSE,  FALSE,
3685     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
3686   },
3687   {
3688     Ytank_n_e,                          FALSE,  FALSE,
3689     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
3690   },
3691   {
3692     Ytank_e_s,                          FALSE,  FALSE,
3693     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
3694   },
3695   {
3696     Ytank_s_w,                          FALSE,  FALSE,
3697     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
3698   },
3699   {
3700     Ytank_e_n,                          FALSE,  FALSE,
3701     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
3702   },
3703   {
3704     Ytank_s_e,                          FALSE,  FALSE,
3705     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
3706   },
3707   {
3708     Ytank_w_s,                          FALSE,  FALSE,
3709     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
3710   },
3711   {
3712     Ytank_n_w,                          FALSE,  FALSE,
3713     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
3714   },
3715   {
3716     Ytank_stone,                        FALSE,  FALSE,
3717     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
3718   },
3719   {
3720     Ytank_spring,                       FALSE,  FALSE,
3721     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
3722   },
3723   {
3724     Xandroid,                           TRUE,   FALSE,
3725     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
3726   },
3727   {
3728     Xandroid_1_n,                       FALSE,  FALSE,
3729     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
3730   },
3731   {
3732     Xandroid_2_n,                       FALSE,  FALSE,
3733     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
3734   },
3735   {
3736     Xandroid_1_e,                       FALSE,  FALSE,
3737     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
3738   },
3739   {
3740     Xandroid_2_e,                       FALSE,  FALSE,
3741     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
3742   },
3743   {
3744     Xandroid_1_w,                       FALSE,  FALSE,
3745     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
3746   },
3747   {
3748     Xandroid_2_w,                       FALSE,  FALSE,
3749     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
3750   },
3751   {
3752     Xandroid_1_s,                       FALSE,  FALSE,
3753     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
3754   },
3755   {
3756     Xandroid_2_s,                       FALSE,  FALSE,
3757     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
3758   },
3759   {
3760     Yandroid_n,                         FALSE,  FALSE,
3761     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
3762   },
3763   {
3764     Yandroid_nB,                        FALSE,  TRUE,
3765     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
3766   },
3767   {
3768     Yandroid_ne,                        FALSE,  FALSE,
3769     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
3770   },
3771   {
3772     Yandroid_neB,                       FALSE,  TRUE,
3773     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
3774   },
3775   {
3776     Yandroid_e,                         FALSE,  FALSE,
3777     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
3778   },
3779   {
3780     Yandroid_eB,                        FALSE,  TRUE,
3781     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
3782   },
3783   {
3784     Yandroid_se,                        FALSE,  FALSE,
3785     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
3786   },
3787   {
3788     Yandroid_seB,                       FALSE,  TRUE,
3789     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
3790   },
3791   {
3792     Yandroid_s,                         FALSE,  FALSE,
3793     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
3794   },
3795   {
3796     Yandroid_sB,                        FALSE,  TRUE,
3797     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
3798   },
3799   {
3800     Yandroid_sw,                        FALSE,  FALSE,
3801     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
3802   },
3803   {
3804     Yandroid_swB,                       FALSE,  TRUE,
3805     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
3806   },
3807   {
3808     Yandroid_w,                         FALSE,  FALSE,
3809     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
3810   },
3811   {
3812     Yandroid_wB,                        FALSE,  TRUE,
3813     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
3814   },
3815   {
3816     Yandroid_nw,                        FALSE,  FALSE,
3817     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
3818   },
3819   {
3820     Yandroid_nwB,                       FALSE,  TRUE,
3821     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
3822   },
3823   {
3824     Xspring,                            TRUE,   FALSE,
3825     EL_SPRING,                          -1, -1
3826   },
3827   {
3828     Xspring_pause,                      FALSE,  FALSE,
3829     EL_SPRING,                          -1, -1
3830   },
3831   {
3832     Xspring_e,                          FALSE,  FALSE,
3833     EL_SPRING,                          -1, -1
3834   },
3835   {
3836     Xspring_w,                          FALSE,  FALSE,
3837     EL_SPRING,                          -1, -1
3838   },
3839   {
3840     Xspring_fall,                       FALSE,  FALSE,
3841     EL_SPRING,                          -1, -1
3842   },
3843   {
3844     Yspring_s,                          FALSE,  FALSE,
3845     EL_SPRING,                          ACTION_FALLING, -1
3846   },
3847   {
3848     Yspring_sB,                         FALSE,  TRUE,
3849     EL_SPRING,                          ACTION_FALLING, -1
3850   },
3851   {
3852     Yspring_e,                          FALSE,  FALSE,
3853     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
3854   },
3855   {
3856     Yspring_eB,                         FALSE,  TRUE,
3857     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
3858   },
3859   {
3860     Yspring_w,                          FALSE,  FALSE,
3861     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
3862   },
3863   {
3864     Yspring_wB,                         FALSE,  TRUE,
3865     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
3866   },
3867   {
3868     Yspring_kill_e,                     FALSE,  FALSE,
3869     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
3870   },
3871   {
3872     Yspring_kill_eB,                    FALSE,  TRUE,
3873     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
3874   },
3875   {
3876     Yspring_kill_w,                     FALSE,  FALSE,
3877     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
3878   },
3879   {
3880     Yspring_kill_wB,                    FALSE,  TRUE,
3881     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
3882   },
3883   {
3884     Xeater_n,                           TRUE,   FALSE,
3885     EL_YAMYAM_UP,                       -1, -1
3886   },
3887   {
3888     Xeater_e,                           TRUE,   FALSE,
3889     EL_YAMYAM_RIGHT,                    -1, -1
3890   },
3891   {
3892     Xeater_w,                           TRUE,   FALSE,
3893     EL_YAMYAM_LEFT,                     -1, -1
3894   },
3895   {
3896     Xeater_s,                           TRUE,   FALSE,
3897     EL_YAMYAM_DOWN,                     -1, -1
3898   },
3899   {
3900     Yeater_n,                           FALSE,  FALSE,
3901     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
3902   },
3903   {
3904     Yeater_nB,                          FALSE,  TRUE,
3905     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
3906   },
3907   {
3908     Yeater_e,                           FALSE,  FALSE,
3909     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
3910   },
3911   {
3912     Yeater_eB,                          FALSE,  TRUE,
3913     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
3914   },
3915   {
3916     Yeater_s,                           FALSE,  FALSE,
3917     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
3918   },
3919   {
3920     Yeater_sB,                          FALSE,  TRUE,
3921     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
3922   },
3923   {
3924     Yeater_w,                           FALSE,  FALSE,
3925     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
3926   },
3927   {
3928     Yeater_wB,                          FALSE,  TRUE,
3929     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
3930   },
3931   {
3932     Yeater_stone,                       FALSE,  FALSE,
3933     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
3934   },
3935   {
3936     Yeater_spring,                      FALSE,  FALSE,
3937     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
3938   },
3939   {
3940     Xalien,                             TRUE,   FALSE,
3941     EL_ROBOT,                           -1, -1
3942   },
3943   {
3944     Xalien_pause,                       FALSE,  FALSE,
3945     EL_ROBOT,                           -1, -1
3946   },
3947   {
3948     Yalien_n,                           FALSE,  FALSE,
3949     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
3950   },
3951   {
3952     Yalien_nB,                          FALSE,  TRUE,
3953     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
3954   },
3955   {
3956     Yalien_e,                           FALSE,  FALSE,
3957     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
3958   },
3959   {
3960     Yalien_eB,                          FALSE,  TRUE,
3961     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
3962   },
3963   {
3964     Yalien_s,                           FALSE,  FALSE,
3965     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
3966   },
3967   {
3968     Yalien_sB,                          FALSE,  TRUE,
3969     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
3970   },
3971   {
3972     Yalien_w,                           FALSE,  FALSE,
3973     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
3974   },
3975   {
3976     Yalien_wB,                          FALSE,  TRUE,
3977     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
3978   },
3979   {
3980     Yalien_stone,                       FALSE,  FALSE,
3981     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
3982   },
3983   {
3984     Yalien_spring,                      FALSE,  FALSE,
3985     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
3986   },
3987   {
3988     Xemerald,                           TRUE,   FALSE,
3989     EL_EMERALD,                         -1, -1
3990   },
3991   {
3992     Xemerald_pause,                     FALSE,  FALSE,
3993     EL_EMERALD,                         -1, -1
3994   },
3995   {
3996     Xemerald_fall,                      FALSE,  FALSE,
3997     EL_EMERALD,                         -1, -1
3998   },
3999   {
4000     Xemerald_shine,                     FALSE,  FALSE,
4001     EL_EMERALD,                         ACTION_TWINKLING, -1
4002   },
4003   {
4004     Yemerald_s,                         FALSE,  FALSE,
4005     EL_EMERALD,                         ACTION_FALLING, -1
4006   },
4007   {
4008     Yemerald_sB,                        FALSE,  TRUE,
4009     EL_EMERALD,                         ACTION_FALLING, -1
4010   },
4011   {
4012     Yemerald_e,                         FALSE,  FALSE,
4013     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
4014   },
4015   {
4016     Yemerald_eB,                        FALSE,  TRUE,
4017     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
4018   },
4019   {
4020     Yemerald_w,                         FALSE,  FALSE,
4021     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
4022   },
4023   {
4024     Yemerald_wB,                        FALSE,  TRUE,
4025     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
4026   },
4027   {
4028     Yemerald_eat,                       FALSE,  FALSE,
4029     EL_EMERALD,                         ACTION_COLLECTING, -1
4030   },
4031   {
4032     Yemerald_stone,                     FALSE,  FALSE,
4033     EL_NUT,                             ACTION_BREAKING, -1
4034   },
4035   {
4036     Xdiamond,                           TRUE,   FALSE,
4037     EL_DIAMOND,                         -1, -1
4038   },
4039   {
4040     Xdiamond_pause,                     FALSE,  FALSE,
4041     EL_DIAMOND,                         -1, -1
4042   },
4043   {
4044     Xdiamond_fall,                      FALSE,  FALSE,
4045     EL_DIAMOND,                         -1, -1
4046   },
4047   {
4048     Xdiamond_shine,                     FALSE,  FALSE,
4049     EL_DIAMOND,                         ACTION_TWINKLING, -1
4050   },
4051   {
4052     Ydiamond_s,                         FALSE,  FALSE,
4053     EL_DIAMOND,                         ACTION_FALLING, -1
4054   },
4055   {
4056     Ydiamond_sB,                        FALSE,  TRUE,
4057     EL_DIAMOND,                         ACTION_FALLING, -1
4058   },
4059   {
4060     Ydiamond_e,                         FALSE,  FALSE,
4061     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
4062   },
4063   {
4064     Ydiamond_eB,                        FALSE,  TRUE,
4065     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
4066   },
4067   {
4068     Ydiamond_w,                         FALSE,  FALSE,
4069     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
4070   },
4071   {
4072     Ydiamond_wB,                        FALSE,  TRUE,
4073     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
4074   },
4075   {
4076     Ydiamond_eat,                       FALSE,  FALSE,
4077     EL_DIAMOND,                         ACTION_COLLECTING, -1
4078   },
4079   {
4080     Ydiamond_stone,                     FALSE,  FALSE,
4081     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
4082   },
4083   {
4084     Xdrip_fall,                         TRUE,   FALSE,
4085     EL_AMOEBA_DROP,                     -1, -1
4086   },
4087   {
4088     Xdrip_stretch,                      FALSE,  FALSE,
4089     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4090   },
4091   {
4092     Xdrip_stretchB,                     FALSE,  TRUE,
4093     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4094   },
4095   {
4096     Xdrip_eat,                          FALSE,  FALSE,
4097     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
4098   },
4099   {
4100     Ydrip_s1,                           FALSE,  FALSE,
4101     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4102   },
4103   {
4104     Ydrip_s1B,                          FALSE,  TRUE,
4105     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4106   },
4107   {
4108     Ydrip_s2,                           FALSE,  FALSE,
4109     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4110   },
4111   {
4112     Ydrip_s2B,                          FALSE,  TRUE,
4113     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4114   },
4115   {
4116     Xbomb,                              TRUE,   FALSE,
4117     EL_BOMB,                            -1, -1
4118   },
4119   {
4120     Xbomb_pause,                        FALSE,  FALSE,
4121     EL_BOMB,                            -1, -1
4122   },
4123   {
4124     Xbomb_fall,                         FALSE,  FALSE,
4125     EL_BOMB,                            -1, -1
4126   },
4127   {
4128     Ybomb_s,                            FALSE,  FALSE,
4129     EL_BOMB,                            ACTION_FALLING, -1
4130   },
4131   {
4132     Ybomb_sB,                           FALSE,  TRUE,
4133     EL_BOMB,                            ACTION_FALLING, -1
4134   },
4135   {
4136     Ybomb_e,                            FALSE,  FALSE,
4137     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
4138   },
4139   {
4140     Ybomb_eB,                           FALSE,  TRUE,
4141     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
4142   },
4143   {
4144     Ybomb_w,                            FALSE,  FALSE,
4145     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
4146   },
4147   {
4148     Ybomb_wB,                           FALSE,  TRUE,
4149     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
4150   },
4151   {
4152     Ybomb_eat,                          FALSE,  FALSE,
4153     EL_BOMB,                            ACTION_ACTIVATING, -1
4154   },
4155   {
4156     Xballoon,                           TRUE,   FALSE,
4157     EL_BALLOON,                         -1, -1
4158   },
4159   {
4160     Yballoon_n,                         FALSE,  FALSE,
4161     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
4162   },
4163   {
4164     Yballoon_nB,                        FALSE,  TRUE,
4165     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
4166   },
4167   {
4168     Yballoon_e,                         FALSE,  FALSE,
4169     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
4170   },
4171   {
4172     Yballoon_eB,                        FALSE,  TRUE,
4173     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
4174   },
4175   {
4176     Yballoon_s,                         FALSE,  FALSE,
4177     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
4178   },
4179   {
4180     Yballoon_sB,                        FALSE,  TRUE,
4181     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
4182   },
4183   {
4184     Yballoon_w,                         FALSE,  FALSE,
4185     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
4186   },
4187   {
4188     Yballoon_wB,                        FALSE,  TRUE,
4189     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
4190   },
4191   {
4192     Xgrass,                             TRUE,   FALSE,
4193     EL_EMC_GRASS,                       -1, -1
4194   },
4195   {
4196     Ygrass_nB,                          FALSE,  FALSE,
4197     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
4198   },
4199   {
4200     Ygrass_eB,                          FALSE,  FALSE,
4201     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
4202   },
4203   {
4204     Ygrass_sB,                          FALSE,  FALSE,
4205     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
4206   },
4207   {
4208     Ygrass_wB,                          FALSE,  FALSE,
4209     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
4210   },
4211   {
4212     Xdirt,                              TRUE,   FALSE,
4213     EL_SAND,                            -1, -1
4214   },
4215   {
4216     Ydirt_nB,                           FALSE,  FALSE,
4217     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
4218   },
4219   {
4220     Ydirt_eB,                           FALSE,  FALSE,
4221     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
4222   },
4223   {
4224     Ydirt_sB,                           FALSE,  FALSE,
4225     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
4226   },
4227   {
4228     Ydirt_wB,                           FALSE,  FALSE,
4229     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
4230   },
4231   {
4232     Xacid_ne,                           TRUE,   FALSE,
4233     EL_ACID_POOL_TOPRIGHT,              -1, -1
4234   },
4235   {
4236     Xacid_se,                           TRUE,   FALSE,
4237     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
4238   },
4239   {
4240     Xacid_s,                            TRUE,   FALSE,
4241     EL_ACID_POOL_BOTTOM,                -1, -1
4242   },
4243   {
4244     Xacid_sw,                           TRUE,   FALSE,
4245     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
4246   },
4247   {
4248     Xacid_nw,                           TRUE,   FALSE,
4249     EL_ACID_POOL_TOPLEFT,               -1, -1
4250   },
4251   {
4252     Xacid_1,                            TRUE,   FALSE,
4253     EL_ACID,                            -1, -1
4254   },
4255   {
4256     Xacid_2,                            FALSE,  FALSE,
4257     EL_ACID,                            -1, -1
4258   },
4259   {
4260     Xacid_3,                            FALSE,  FALSE,
4261     EL_ACID,                            -1, -1
4262   },
4263   {
4264     Xacid_4,                            FALSE,  FALSE,
4265     EL_ACID,                            -1, -1
4266   },
4267   {
4268     Xacid_5,                            FALSE,  FALSE,
4269     EL_ACID,                            -1, -1
4270   },
4271   {
4272     Xacid_6,                            FALSE,  FALSE,
4273     EL_ACID,                            -1, -1
4274   },
4275   {
4276     Xacid_7,                            FALSE,  FALSE,
4277     EL_ACID,                            -1, -1
4278   },
4279   {
4280     Xacid_8,                            FALSE,  FALSE,
4281     EL_ACID,                            -1, -1
4282   },
4283   {
4284     Xball_1,                            TRUE,   FALSE,
4285     EL_EMC_MAGIC_BALL,                  -1, -1
4286   },
4287   {
4288     Xball_1B,                           FALSE,  FALSE,
4289     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
4290   },
4291   {
4292     Xball_2,                            FALSE,  FALSE,
4293     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
4294   },
4295   {
4296     Xball_2B,                           FALSE,  FALSE,
4297     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
4298   },
4299   {
4300     Yball_eat,                          FALSE,  FALSE,
4301     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
4302   },
4303   {
4304     Ykey_1_eat,                         FALSE,  FALSE,
4305     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
4306   },
4307   {
4308     Ykey_2_eat,                         FALSE,  FALSE,
4309     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
4310   },
4311   {
4312     Ykey_3_eat,                         FALSE,  FALSE,
4313     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
4314   },
4315   {
4316     Ykey_4_eat,                         FALSE,  FALSE,
4317     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
4318   },
4319   {
4320     Ykey_5_eat,                         FALSE,  FALSE,
4321     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
4322   },
4323   {
4324     Ykey_6_eat,                         FALSE,  FALSE,
4325     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
4326   },
4327   {
4328     Ykey_7_eat,                         FALSE,  FALSE,
4329     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
4330   },
4331   {
4332     Ykey_8_eat,                         FALSE,  FALSE,
4333     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
4334   },
4335   {
4336     Ylenses_eat,                        FALSE,  FALSE,
4337     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
4338   },
4339   {
4340     Ymagnify_eat,                       FALSE,  FALSE,
4341     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
4342   },
4343   {
4344     Ygrass_eat,                         FALSE,  FALSE,
4345     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
4346   },
4347   {
4348     Ydirt_eat,                          FALSE,  FALSE,
4349     EL_SAND,                            ACTION_SNAPPING, -1
4350   },
4351   {
4352     Xgrow_ns,                           TRUE,   FALSE,
4353     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
4354   },
4355   {
4356     Ygrow_ns_eat,                       FALSE,  FALSE,
4357     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
4358   },
4359   {
4360     Xgrow_ew,                           TRUE,   FALSE,
4361     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
4362   },
4363   {
4364     Ygrow_ew_eat,                       FALSE,  FALSE,
4365     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
4366   },
4367   {
4368     Xwonderwall,                        TRUE,   FALSE,
4369     EL_MAGIC_WALL,                      -1, -1
4370   },
4371   {
4372     XwonderwallB,                       FALSE,  FALSE,
4373     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
4374   },
4375   {
4376     Xamoeba_1,                          TRUE,   FALSE,
4377     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
4378   },
4379   {
4380     Xamoeba_2,                          FALSE,  FALSE,
4381     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
4382   },
4383   {
4384     Xamoeba_3,                          FALSE,  FALSE,
4385     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
4386   },
4387   {
4388     Xamoeba_4,                          FALSE,  FALSE,
4389     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
4390   },
4391   {
4392     Xamoeba_5,                          TRUE,   FALSE,
4393     EL_AMOEBA_WET,                      ACTION_OTHER, -1
4394   },
4395   {
4396     Xamoeba_6,                          FALSE,  FALSE,
4397     EL_AMOEBA_WET,                      ACTION_OTHER, -1
4398   },
4399   {
4400     Xamoeba_7,                          FALSE,  FALSE,
4401     EL_AMOEBA_WET,                      ACTION_OTHER, -1
4402   },
4403   {
4404     Xamoeba_8,                          FALSE,  FALSE,
4405     EL_AMOEBA_WET,                      ACTION_OTHER, -1
4406   },
4407   {
4408     Xdoor_1,                            TRUE,   FALSE,
4409     EL_EM_GATE_1,                       -1, -1
4410   },
4411   {
4412     Xdoor_2,                            TRUE,   FALSE,
4413     EL_EM_GATE_2,                       -1, -1
4414   },
4415   {
4416     Xdoor_3,                            TRUE,   FALSE,
4417     EL_EM_GATE_3,                       -1, -1
4418   },
4419   {
4420     Xdoor_4,                            TRUE,   FALSE,
4421     EL_EM_GATE_4,                       -1, -1
4422   },
4423   {
4424     Xdoor_5,                            TRUE,   FALSE,
4425     EL_EMC_GATE_5,                      -1, -1
4426   },
4427   {
4428     Xdoor_6,                            TRUE,   FALSE,
4429     EL_EMC_GATE_6,                      -1, -1
4430   },
4431   {
4432     Xdoor_7,                            TRUE,   FALSE,
4433     EL_EMC_GATE_7,                      -1, -1
4434   },
4435   {
4436     Xdoor_8,                            TRUE,   FALSE,
4437     EL_EMC_GATE_8,                      -1, -1
4438   },
4439   {
4440     Xkey_1,                             TRUE,   FALSE,
4441     EL_EM_KEY_1,                        -1, -1
4442   },
4443   {
4444     Xkey_2,                             TRUE,   FALSE,
4445     EL_EM_KEY_2,                        -1, -1
4446   },
4447   {
4448     Xkey_3,                             TRUE,   FALSE,
4449     EL_EM_KEY_3,                        -1, -1
4450   },
4451   {
4452     Xkey_4,                             TRUE,   FALSE,
4453     EL_EM_KEY_4,                        -1, -1
4454   },
4455   {
4456     Xkey_5,                             TRUE,   FALSE,
4457     EL_EMC_KEY_5,                       -1, -1
4458   },
4459   {
4460     Xkey_6,                             TRUE,   FALSE,
4461     EL_EMC_KEY_6,                       -1, -1
4462   },
4463   {
4464     Xkey_7,                             TRUE,   FALSE,
4465     EL_EMC_KEY_7,                       -1, -1
4466   },
4467   {
4468     Xkey_8,                             TRUE,   FALSE,
4469     EL_EMC_KEY_8,                       -1, -1
4470   },
4471   {
4472     Xwind_n,                            TRUE,   FALSE,
4473     EL_BALLOON_SWITCH_UP,               -1, -1
4474   },
4475   {
4476     Xwind_e,                            TRUE,   FALSE,
4477     EL_BALLOON_SWITCH_RIGHT,            -1, -1
4478   },
4479   {
4480     Xwind_s,                            TRUE,   FALSE,
4481     EL_BALLOON_SWITCH_DOWN,             -1, -1
4482   },
4483   {
4484     Xwind_w,                            TRUE,   FALSE,
4485     EL_BALLOON_SWITCH_LEFT,             -1, -1
4486   },
4487   {
4488     Xwind_nesw,                         TRUE,   FALSE,
4489     EL_BALLOON_SWITCH_ANY,              -1, -1
4490   },
4491   {
4492     Xwind_stop,                         TRUE,   FALSE,
4493     EL_BALLOON_SWITCH_NONE,             -1, -1
4494   },
4495   {
4496     Xexit,                              TRUE,   FALSE,
4497     EL_EM_EXIT_CLOSED,                  -1, -1
4498   },
4499   {
4500     Xexit_1,                            TRUE,   FALSE,
4501     EL_EM_EXIT_OPEN,                    -1, -1
4502   },
4503   {
4504     Xexit_2,                            FALSE,  FALSE,
4505     EL_EM_EXIT_OPEN,                    -1, -1
4506   },
4507   {
4508     Xexit_3,                            FALSE,  FALSE,
4509     EL_EM_EXIT_OPEN,                    -1, -1
4510   },
4511   {
4512     Xdynamite,                          TRUE,   FALSE,
4513     EL_EM_DYNAMITE,                     -1, -1
4514   },
4515   {
4516     Ydynamite_eat,                      FALSE,  FALSE,
4517     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
4518   },
4519   {
4520     Xdynamite_1,                        TRUE,   FALSE,
4521     EL_EM_DYNAMITE_ACTIVE,              -1, -1
4522   },
4523   {
4524     Xdynamite_2,                        FALSE,  FALSE,
4525     EL_EM_DYNAMITE_ACTIVE,              -1, -1
4526   },
4527   {
4528     Xdynamite_3,                        FALSE,  FALSE,
4529     EL_EM_DYNAMITE_ACTIVE,              -1, -1
4530   },
4531   {
4532     Xdynamite_4,                        FALSE,  FALSE,
4533     EL_EM_DYNAMITE_ACTIVE,              -1, -1
4534   },
4535   {
4536     Xbumper,                            TRUE,   FALSE,
4537     EL_EMC_SPRING_BUMPER,               -1, -1
4538   },
4539   {
4540     XbumperB,                           FALSE,  FALSE,
4541     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
4542   },
4543   {
4544     Xwheel,                             TRUE,   FALSE,
4545     EL_ROBOT_WHEEL,                     -1, -1
4546   },
4547   {
4548     XwheelB,                            FALSE,  FALSE,
4549     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
4550   },
4551   {
4552     Xswitch,                            TRUE,   FALSE,
4553     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
4554   },
4555   {
4556     XswitchB,                           FALSE,  FALSE,
4557     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
4558   },
4559   {
4560     Xsand,                              TRUE,   FALSE,
4561     EL_QUICKSAND_EMPTY,                 -1, -1
4562   },
4563   {
4564     Xsand_stone,                        TRUE,   FALSE,
4565     EL_QUICKSAND_FULL,                  -1, -1
4566   },
4567   {
4568     Xsand_stonein_1,                    FALSE,  TRUE,
4569     EL_ROCK,                            ACTION_FILLING, -1
4570   },
4571   {
4572     Xsand_stonein_2,                    FALSE,  TRUE,
4573     EL_ROCK,                            ACTION_FILLING, -1
4574   },
4575   {
4576     Xsand_stonein_3,                    FALSE,  TRUE,
4577     EL_ROCK,                            ACTION_FILLING, -1
4578   },
4579   {
4580     Xsand_stonein_4,                    FALSE,  TRUE,
4581     EL_ROCK,                            ACTION_FILLING, -1
4582   },
4583   {
4584     Xsand_stonesand_1,                  FALSE,  FALSE,
4585     EL_QUICKSAND_FULL,                  -1, -1
4586   },
4587   {
4588     Xsand_stonesand_2,                  FALSE,  FALSE,
4589     EL_QUICKSAND_FULL,                  -1, -1
4590   },
4591   {
4592     Xsand_stonesand_3,                  FALSE,  FALSE,
4593     EL_QUICKSAND_FULL,                  -1, -1
4594   },
4595   {
4596     Xsand_stonesand_4,                  FALSE,  FALSE,
4597     EL_QUICKSAND_FULL,                  -1, -1
4598   },
4599   {
4600     Xsand_stoneout_1,                   FALSE,  FALSE,
4601     EL_ROCK,                            ACTION_EMPTYING, -1
4602   },
4603   {
4604     Xsand_stoneout_2,                   FALSE,  FALSE,
4605     EL_ROCK,                            ACTION_EMPTYING, -1
4606   },
4607   {
4608     Xsand_sandstone_1,                  FALSE,  FALSE,
4609     EL_QUICKSAND_FULL,                  -1, -1
4610   },
4611   {
4612     Xsand_sandstone_2,                  FALSE,  FALSE,
4613     EL_QUICKSAND_FULL,                  -1, -1
4614   },
4615   {
4616     Xsand_sandstone_3,                  FALSE,  FALSE,
4617     EL_QUICKSAND_FULL,                  -1, -1
4618   },
4619   {
4620     Xsand_sandstone_4,                  FALSE,  FALSE,
4621     EL_QUICKSAND_FULL,                  -1, -1
4622   },
4623   {
4624     Xplant,                             TRUE,   FALSE,
4625     EL_EMC_PLANT,                       -1, -1
4626   },
4627   {
4628     Yplant,                             FALSE,  FALSE,
4629     EL_EMC_PLANT,                       -1, -1
4630   },
4631   {
4632     Xlenses,                            TRUE,   FALSE,
4633     EL_EMC_LENSES,                      -1, -1
4634   },
4635   {
4636     Xmagnify,                           TRUE,   FALSE,
4637     EL_EMC_MAGNIFIER,                   -1, -1
4638   },
4639   {
4640     Xdripper,                           TRUE,   FALSE,
4641     EL_EMC_DRIPPER,                     -1, -1
4642   },
4643   {
4644     XdripperB,                          FALSE,  FALSE,
4645     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
4646   },
4647   {
4648     Xfake_blank,                        TRUE,   FALSE,
4649     EL_INVISIBLE_WALL,                  -1, -1
4650   },
4651   {
4652     Xfake_blankB,                       FALSE,  FALSE,
4653     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
4654   },
4655   {
4656     Xfake_grass,                        TRUE,   FALSE,
4657     EL_EMC_FAKE_GRASS,                  -1, -1
4658   },
4659   {
4660     Xfake_grassB,                       FALSE,  FALSE,
4661     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
4662   },
4663   {
4664     Xfake_door_1,                       TRUE,   FALSE,
4665     EL_EM_GATE_1_GRAY,                  -1, -1
4666   },
4667   {
4668     Xfake_door_2,                       TRUE,   FALSE,
4669     EL_EM_GATE_2_GRAY,                  -1, -1
4670   },
4671   {
4672     Xfake_door_3,                       TRUE,   FALSE,
4673     EL_EM_GATE_3_GRAY,                  -1, -1
4674   },
4675   {
4676     Xfake_door_4,                       TRUE,   FALSE,
4677     EL_EM_GATE_4_GRAY,                  -1, -1
4678   },
4679   {
4680     Xfake_door_5,                       TRUE,   FALSE,
4681     EL_EMC_GATE_5_GRAY,                 -1, -1
4682   },
4683   {
4684     Xfake_door_6,                       TRUE,   FALSE,
4685     EL_EMC_GATE_6_GRAY,                 -1, -1
4686   },
4687   {
4688     Xfake_door_7,                       TRUE,   FALSE,
4689     EL_EMC_GATE_7_GRAY,                 -1, -1
4690   },
4691   {
4692     Xfake_door_8,                       TRUE,   FALSE,
4693     EL_EMC_GATE_8_GRAY,                 -1, -1
4694   },
4695   {
4696     Xfake_acid_1,                       TRUE,   FALSE,
4697     EL_EMC_FAKE_ACID,                   -1, -1
4698   },
4699   {
4700     Xfake_acid_2,                       FALSE,  FALSE,
4701     EL_EMC_FAKE_ACID,                   -1, -1
4702   },
4703   {
4704     Xfake_acid_3,                       FALSE,  FALSE,
4705     EL_EMC_FAKE_ACID,                   -1, -1
4706   },
4707   {
4708     Xfake_acid_4,                       FALSE,  FALSE,
4709     EL_EMC_FAKE_ACID,                   -1, -1
4710   },
4711   {
4712     Xfake_acid_5,                       FALSE,  FALSE,
4713     EL_EMC_FAKE_ACID,                   -1, -1
4714   },
4715   {
4716     Xfake_acid_6,                       FALSE,  FALSE,
4717     EL_EMC_FAKE_ACID,                   -1, -1
4718   },
4719   {
4720     Xfake_acid_7,                       FALSE,  FALSE,
4721     EL_EMC_FAKE_ACID,                   -1, -1
4722   },
4723   {
4724     Xfake_acid_8,                       FALSE,  FALSE,
4725     EL_EMC_FAKE_ACID,                   -1, -1
4726   },
4727   {
4728     Xsteel_1,                           TRUE,   FALSE,
4729     EL_STEELWALL,                       -1, -1
4730   },
4731   {
4732     Xsteel_2,                           TRUE,   FALSE,
4733     EL_EMC_STEELWALL_2,                 -1, -1
4734   },
4735   {
4736     Xsteel_3,                           TRUE,   FALSE,
4737     EL_EMC_STEELWALL_3,                 -1, -1
4738   },
4739   {
4740     Xsteel_4,                           TRUE,   FALSE,
4741     EL_EMC_STEELWALL_4,                 -1, -1
4742   },
4743   {
4744     Xwall_1,                            TRUE,   FALSE,
4745     EL_WALL,                            -1, -1
4746   },
4747   {
4748     Xwall_2,                            TRUE,   FALSE,
4749     EL_EMC_WALL_14,                     -1, -1
4750   },
4751   {
4752     Xwall_3,                            TRUE,   FALSE,
4753     EL_EMC_WALL_15,                     -1, -1
4754   },
4755   {
4756     Xwall_4,                            TRUE,   FALSE,
4757     EL_EMC_WALL_16,                     -1, -1
4758   },
4759   {
4760     Xround_wall_1,                      TRUE,   FALSE,
4761     EL_WALL_SLIPPERY,                   -1, -1
4762   },
4763   {
4764     Xround_wall_2,                      TRUE,   FALSE,
4765     EL_EMC_WALL_SLIPPERY_2,             -1, -1
4766   },
4767   {
4768     Xround_wall_3,                      TRUE,   FALSE,
4769     EL_EMC_WALL_SLIPPERY_3,             -1, -1
4770   },
4771   {
4772     Xround_wall_4,                      TRUE,   FALSE,
4773     EL_EMC_WALL_SLIPPERY_4,             -1, -1
4774   },
4775   {
4776     Xdecor_1,                           TRUE,   FALSE,
4777     EL_EMC_WALL_8,                      -1, -1
4778   },
4779   {
4780     Xdecor_2,                           TRUE,   FALSE,
4781     EL_EMC_WALL_6,                      -1, -1
4782   },
4783   {
4784     Xdecor_3,                           TRUE,   FALSE,
4785     EL_EMC_WALL_4,                      -1, -1
4786   },
4787   {
4788     Xdecor_4,                           TRUE,   FALSE,
4789     EL_EMC_WALL_7,                      -1, -1
4790   },
4791   {
4792     Xdecor_5,                           TRUE,   FALSE,
4793     EL_EMC_WALL_5,                      -1, -1
4794   },
4795   {
4796     Xdecor_6,                           TRUE,   FALSE,
4797     EL_EMC_WALL_9,                      -1, -1
4798   },
4799   {
4800     Xdecor_7,                           TRUE,   FALSE,
4801     EL_EMC_WALL_10,                     -1, -1
4802   },
4803   {
4804     Xdecor_8,                           TRUE,   FALSE,
4805     EL_EMC_WALL_1,                      -1, -1
4806   },
4807   {
4808     Xdecor_9,                           TRUE,   FALSE,
4809     EL_EMC_WALL_2,                      -1, -1
4810   },
4811   {
4812     Xdecor_10,                          TRUE,   FALSE,
4813     EL_EMC_WALL_3,                      -1, -1
4814   },
4815   {
4816     Xdecor_11,                          TRUE,   FALSE,
4817     EL_EMC_WALL_11,                     -1, -1
4818   },
4819   {
4820     Xdecor_12,                          TRUE,   FALSE,
4821     EL_EMC_WALL_12,                     -1, -1
4822   },
4823   {
4824     Xalpha_0,                           TRUE,   FALSE,
4825     EL_CHAR('0'),                       -1, -1
4826   },
4827   {
4828     Xalpha_1,                           TRUE,   FALSE,
4829     EL_CHAR('1'),                       -1, -1
4830   },
4831   {
4832     Xalpha_2,                           TRUE,   FALSE,
4833     EL_CHAR('2'),                       -1, -1
4834   },
4835   {
4836     Xalpha_3,                           TRUE,   FALSE,
4837     EL_CHAR('3'),                       -1, -1
4838   },
4839   {
4840     Xalpha_4,                           TRUE,   FALSE,
4841     EL_CHAR('4'),                       -1, -1
4842   },
4843   {
4844     Xalpha_5,                           TRUE,   FALSE,
4845     EL_CHAR('5'),                       -1, -1
4846   },
4847   {
4848     Xalpha_6,                           TRUE,   FALSE,
4849     EL_CHAR('6'),                       -1, -1
4850   },
4851   {
4852     Xalpha_7,                           TRUE,   FALSE,
4853     EL_CHAR('7'),                       -1, -1
4854   },
4855   {
4856     Xalpha_8,                           TRUE,   FALSE,
4857     EL_CHAR('8'),                       -1, -1
4858   },
4859   {
4860     Xalpha_9,                           TRUE,   FALSE,
4861     EL_CHAR('9'),                       -1, -1
4862   },
4863   {
4864     Xalpha_excla,                       TRUE,   FALSE,
4865     EL_CHAR('!'),                       -1, -1
4866   },
4867   {
4868     Xalpha_quote,                       TRUE,   FALSE,
4869     EL_CHAR('"'),                       -1, -1
4870   },
4871   {
4872     Xalpha_comma,                       TRUE,   FALSE,
4873     EL_CHAR(','),                       -1, -1
4874   },
4875   {
4876     Xalpha_minus,                       TRUE,   FALSE,
4877     EL_CHAR('-'),                       -1, -1
4878   },
4879   {
4880     Xalpha_perio,                       TRUE,   FALSE,
4881     EL_CHAR('.'),                       -1, -1
4882   },
4883   {
4884     Xalpha_colon,                       TRUE,   FALSE,
4885     EL_CHAR(':'),                       -1, -1
4886   },
4887   {
4888     Xalpha_quest,                       TRUE,   FALSE,
4889     EL_CHAR('?'),                       -1, -1
4890   },
4891   {
4892     Xalpha_a,                           TRUE,   FALSE,
4893     EL_CHAR('A'),                       -1, -1
4894   },
4895   {
4896     Xalpha_b,                           TRUE,   FALSE,
4897     EL_CHAR('B'),                       -1, -1
4898   },
4899   {
4900     Xalpha_c,                           TRUE,   FALSE,
4901     EL_CHAR('C'),                       -1, -1
4902   },
4903   {
4904     Xalpha_d,                           TRUE,   FALSE,
4905     EL_CHAR('D'),                       -1, -1
4906   },
4907   {
4908     Xalpha_e,                           TRUE,   FALSE,
4909     EL_CHAR('E'),                       -1, -1
4910   },
4911   {
4912     Xalpha_f,                           TRUE,   FALSE,
4913     EL_CHAR('F'),                       -1, -1
4914   },
4915   {
4916     Xalpha_g,                           TRUE,   FALSE,
4917     EL_CHAR('G'),                       -1, -1
4918   },
4919   {
4920     Xalpha_h,                           TRUE,   FALSE,
4921     EL_CHAR('H'),                       -1, -1
4922   },
4923   {
4924     Xalpha_i,                           TRUE,   FALSE,
4925     EL_CHAR('I'),                       -1, -1
4926   },
4927   {
4928     Xalpha_j,                           TRUE,   FALSE,
4929     EL_CHAR('J'),                       -1, -1
4930   },
4931   {
4932     Xalpha_k,                           TRUE,   FALSE,
4933     EL_CHAR('K'),                       -1, -1
4934   },
4935   {
4936     Xalpha_l,                           TRUE,   FALSE,
4937     EL_CHAR('L'),                       -1, -1
4938   },
4939   {
4940     Xalpha_m,                           TRUE,   FALSE,
4941     EL_CHAR('M'),                       -1, -1
4942   },
4943   {
4944     Xalpha_n,                           TRUE,   FALSE,
4945     EL_CHAR('N'),                       -1, -1
4946   },
4947   {
4948     Xalpha_o,                           TRUE,   FALSE,
4949     EL_CHAR('O'),                       -1, -1
4950   },
4951   {
4952     Xalpha_p,                           TRUE,   FALSE,
4953     EL_CHAR('P'),                       -1, -1
4954   },
4955   {
4956     Xalpha_q,                           TRUE,   FALSE,
4957     EL_CHAR('Q'),                       -1, -1
4958   },
4959   {
4960     Xalpha_r,                           TRUE,   FALSE,
4961     EL_CHAR('R'),                       -1, -1
4962   },
4963   {
4964     Xalpha_s,                           TRUE,   FALSE,
4965     EL_CHAR('S'),                       -1, -1
4966   },
4967   {
4968     Xalpha_t,                           TRUE,   FALSE,
4969     EL_CHAR('T'),                       -1, -1
4970   },
4971   {
4972     Xalpha_u,                           TRUE,   FALSE,
4973     EL_CHAR('U'),                       -1, -1
4974   },
4975   {
4976     Xalpha_v,                           TRUE,   FALSE,
4977     EL_CHAR('V'),                       -1, -1
4978   },
4979   {
4980     Xalpha_w,                           TRUE,   FALSE,
4981     EL_CHAR('W'),                       -1, -1
4982   },
4983   {
4984     Xalpha_x,                           TRUE,   FALSE,
4985     EL_CHAR('X'),                       -1, -1
4986   },
4987   {
4988     Xalpha_y,                           TRUE,   FALSE,
4989     EL_CHAR('Y'),                       -1, -1
4990   },
4991   {
4992     Xalpha_z,                           TRUE,   FALSE,
4993     EL_CHAR('Z'),                       -1, -1
4994   },
4995   {
4996     Xalpha_arrow_e,                     TRUE,   FALSE,
4997     EL_CHAR('>'),                       -1, -1
4998   },
4999   {
5000     Xalpha_arrow_w,                     TRUE,   FALSE,
5001     EL_CHAR('<'),                       -1, -1
5002   },
5003   {
5004     Xalpha_copyr,                       TRUE,   FALSE,
5005     EL_CHAR('©'),                       -1, -1
5006   },
5007
5008   {
5009     Xboom_bug,                          FALSE,  FALSE,
5010     EL_BUG,                             ACTION_EXPLODING, -1
5011   },
5012   {
5013     Xboom_bomb,                         FALSE,  FALSE,
5014     EL_BOMB,                            ACTION_EXPLODING, -1
5015   },
5016   {
5017     Xboom_android,                      FALSE,  FALSE,
5018     EL_EMC_ANDROID,                     ACTION_OTHER, -1
5019   },
5020   {
5021     Xboom_1,                            FALSE,  FALSE,
5022     EL_DEFAULT,                         ACTION_EXPLODING, -1
5023   },
5024   {
5025     Xboom_2,                            FALSE,  FALSE,
5026     EL_DEFAULT,                         ACTION_EXPLODING, -1
5027   },
5028   {
5029     Znormal,                            FALSE,  FALSE,
5030     EL_EMPTY,                           -1, -1
5031   },
5032   {
5033     Zdynamite,                          FALSE,  FALSE,
5034     EL_EMPTY,                           -1, -1
5035   },
5036   {
5037     Zplayer,                            FALSE,  FALSE,
5038     EL_EMPTY,                           -1, -1
5039   },
5040   {
5041     ZBORDER,                            FALSE,  FALSE,
5042     EL_EMPTY,                           -1, -1
5043   },
5044
5045   {
5046     -1,                                 FALSE,  FALSE,
5047     -1,                                 -1, -1
5048   }
5049 };
5050
5051 static struct Mapping_EM_to_RND_player
5052 {
5053   int action_em;
5054   int player_nr;
5055
5056   int element_rnd;
5057   int action;
5058   int direction;
5059 }
5060 em_player_mapping_list[] =
5061 {
5062   {
5063     SPR_walk + 0,                       0,
5064     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
5065   },
5066   {
5067     SPR_walk + 1,                       0,
5068     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
5069   },
5070   {
5071     SPR_walk + 2,                       0,
5072     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
5073   },
5074   {
5075     SPR_walk + 3,                       0,
5076     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
5077   },
5078   {
5079     SPR_push + 0,                       0,
5080     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
5081   },
5082   {
5083     SPR_push + 1,                       0,
5084     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
5085   },
5086   {
5087     SPR_push + 2,                       0,
5088     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
5089   },
5090   {
5091     SPR_push + 3,                       0,
5092     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
5093   },
5094   {
5095     SPR_spray + 0,                      0,
5096     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
5097   },
5098   {
5099     SPR_spray + 1,                      0,
5100     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
5101   },
5102   {
5103     SPR_spray + 2,                      0,
5104     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
5105   },
5106   {
5107     SPR_spray + 3,                      0,
5108     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
5109   },
5110   {
5111     SPR_walk + 0,                       1,
5112     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
5113   },
5114   {
5115     SPR_walk + 1,                       1,
5116     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
5117   },
5118   {
5119     SPR_walk + 2,                       1,
5120     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
5121   },
5122   {
5123     SPR_walk + 3,                       1,
5124     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
5125   },
5126   {
5127     SPR_push + 0,                       1,
5128     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
5129   },
5130   {
5131     SPR_push + 1,                       1,
5132     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
5133   },
5134   {
5135     SPR_push + 2,                       1,
5136     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
5137   },
5138   {
5139     SPR_push + 3,                       1,
5140     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
5141   },
5142   {
5143     SPR_spray + 0,                      1,
5144     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
5145   },
5146   {
5147     SPR_spray + 1,                      1,
5148     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
5149   },
5150   {
5151     SPR_spray + 2,                      1,
5152     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
5153   },
5154   {
5155     SPR_spray + 3,                      1,
5156     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
5157   },
5158   {
5159     SPR_still,                          0,
5160     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
5161   },
5162   {
5163     SPR_still,                          1,
5164     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
5165   },
5166   {
5167     SPR_walk + 0,                       2,
5168     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
5169   },
5170   {
5171     SPR_walk + 1,                       2,
5172     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
5173   },
5174   {
5175     SPR_walk + 2,                       2,
5176     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
5177   },
5178   {
5179     SPR_walk + 3,                       2,
5180     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
5181   },
5182   {
5183     SPR_push + 0,                       2,
5184     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
5185   },
5186   {
5187     SPR_push + 1,                       2,
5188     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
5189   },
5190   {
5191     SPR_push + 2,                       2,
5192     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
5193   },
5194   {
5195     SPR_push + 3,                       2,
5196     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
5197   },
5198   {
5199     SPR_spray + 0,                      2,
5200     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
5201   },
5202   {
5203     SPR_spray + 1,                      2,
5204     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
5205   },
5206   {
5207     SPR_spray + 2,                      2,
5208     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
5209   },
5210   {
5211     SPR_spray + 3,                      2,
5212     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
5213   },
5214   {
5215     SPR_walk + 0,                       3,
5216     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
5217   },
5218   {
5219     SPR_walk + 1,                       3,
5220     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
5221   },
5222   {
5223     SPR_walk + 2,                       3,
5224     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
5225   },
5226   {
5227     SPR_walk + 3,                       3,
5228     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
5229   },
5230   {
5231     SPR_push + 0,                       3,
5232     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
5233   },
5234   {
5235     SPR_push + 1,                       3,
5236     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
5237   },
5238   {
5239     SPR_push + 2,                       3,
5240     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
5241   },
5242   {
5243     SPR_push + 3,                       3,
5244     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
5245   },
5246   {
5247     SPR_spray + 0,                      3,
5248     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
5249   },
5250   {
5251     SPR_spray + 1,                      3,
5252     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
5253   },
5254   {
5255     SPR_spray + 2,                      3,
5256     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
5257   },
5258   {
5259     SPR_spray + 3,                      3,
5260     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
5261   },
5262   {
5263     SPR_still,                          2,
5264     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
5265   },
5266   {
5267     SPR_still,                          3,
5268     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
5269   },
5270
5271   {
5272     -1,                                 -1,
5273     -1,                                 -1, -1
5274   }
5275 };
5276
5277 int map_element_RND_to_EM(int element_rnd)
5278 {
5279   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
5280   static boolean mapping_initialized = FALSE;
5281
5282   if (!mapping_initialized)
5283   {
5284     int i;
5285
5286     /* return "Xalpha_quest" for all undefined elements in mapping array */
5287     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5288       mapping_RND_to_EM[i] = Xalpha_quest;
5289
5290     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
5291       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
5292         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
5293           em_object_mapping_list[i].element_em;
5294
5295     mapping_initialized = TRUE;
5296   }
5297
5298   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
5299     return mapping_RND_to_EM[element_rnd];
5300
5301   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
5302
5303   return EL_UNKNOWN;
5304 }
5305
5306 int map_element_EM_to_RND(int element_em)
5307 {
5308   static unsigned short mapping_EM_to_RND[TILE_MAX];
5309   static boolean mapping_initialized = FALSE;
5310
5311   if (!mapping_initialized)
5312   {
5313     int i;
5314
5315     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
5316     for (i = 0; i < TILE_MAX; i++)
5317       mapping_EM_to_RND[i] = EL_UNKNOWN;
5318
5319     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
5320       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
5321         em_object_mapping_list[i].element_rnd;
5322
5323     mapping_initialized = TRUE;
5324   }
5325
5326   if (element_em >= 0 && element_em < TILE_MAX)
5327     return mapping_EM_to_RND[element_em];
5328
5329   Error(ERR_WARN, "invalid EM level element %d", element_em);
5330
5331   return EL_UNKNOWN;
5332 }
5333
5334 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
5335 {
5336   struct LevelInfo_EM *level_em = level->native_em_level;
5337   struct LEVEL *lev = level_em->lev;
5338   int i, j;
5339
5340   for (i = 0; i < TILE_MAX; i++)
5341     lev->android_array[i] = Xblank;
5342
5343   for (i = 0; i < level->num_android_clone_elements; i++)
5344   {
5345     int element_rnd = level->android_clone_element[i];
5346     int element_em = map_element_RND_to_EM(element_rnd);
5347
5348     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
5349       if (em_object_mapping_list[j].element_rnd == element_rnd)
5350         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
5351   }
5352 }
5353
5354 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
5355 {
5356   struct LevelInfo_EM *level_em = level->native_em_level;
5357   struct LEVEL *lev = level_em->lev;
5358   int i, j;
5359
5360   level->num_android_clone_elements = 0;
5361
5362   for (i = 0; i < TILE_MAX; i++)
5363   {
5364     int element_em = lev->android_array[i];
5365     int element_rnd;
5366     boolean element_found = FALSE;
5367
5368     if (element_em == Xblank)
5369       continue;
5370
5371     element_rnd = map_element_EM_to_RND(element_em);
5372
5373     for (j = 0; j < level->num_android_clone_elements; j++)
5374       if (level->android_clone_element[j] == element_rnd)
5375         element_found = TRUE;
5376
5377     if (!element_found)
5378     {
5379       level->android_clone_element[level->num_android_clone_elements++] =
5380         element_rnd;
5381
5382       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
5383         break;
5384     }
5385   }
5386
5387   if (level->num_android_clone_elements == 0)
5388   {
5389     level->num_android_clone_elements = 1;
5390     level->android_clone_element[0] = EL_EMPTY;
5391   }
5392 }
5393
5394 int map_direction_RND_to_EM(int direction)
5395 {
5396   return (direction == MV_UP    ? 0 :
5397           direction == MV_RIGHT ? 1 :
5398           direction == MV_DOWN  ? 2 :
5399           direction == MV_LEFT  ? 3 :
5400           -1);
5401 }
5402
5403 int map_direction_EM_to_RND(int direction)
5404 {
5405   return (direction == 0 ? MV_UP    :
5406           direction == 1 ? MV_RIGHT :
5407           direction == 2 ? MV_DOWN  :
5408           direction == 3 ? MV_LEFT  :
5409           MV_NONE);
5410 }
5411
5412 int get_next_element(int element)
5413 {
5414   switch (element)
5415   {
5416     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
5417     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
5418     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
5419     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
5420     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
5421     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
5422     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
5423     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
5424     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
5425     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
5426     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
5427
5428     default:                            return element;
5429   }
5430 }
5431
5432 #if 0
5433 int el_act_dir2img(int element, int action, int direction)
5434 {
5435   element = GFX_ELEMENT(element);
5436
5437   if (direction == MV_NONE)
5438     return element_info[element].graphic[action];
5439
5440   direction = MV_DIR_TO_BIT(direction);
5441
5442   return element_info[element].direction_graphic[action][direction];
5443 }
5444 #else
5445 int el_act_dir2img(int element, int action, int direction)
5446 {
5447   element = GFX_ELEMENT(element);
5448   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
5449
5450   /* direction_graphic[][] == graphic[] for undefined direction graphics */
5451   return element_info[element].direction_graphic[action][direction];
5452 }
5453 #endif
5454
5455 #if 0
5456 static int el_act_dir2crm(int element, int action, int direction)
5457 {
5458   element = GFX_ELEMENT(element);
5459
5460   if (direction == MV_NONE)
5461     return element_info[element].crumbled[action];
5462
5463   direction = MV_DIR_TO_BIT(direction);
5464
5465   return element_info[element].direction_crumbled[action][direction];
5466 }
5467 #else
5468 static int el_act_dir2crm(int element, int action, int direction)
5469 {
5470   element = GFX_ELEMENT(element);
5471   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
5472
5473   /* direction_graphic[][] == graphic[] for undefined direction graphics */
5474   return element_info[element].direction_crumbled[action][direction];
5475 }
5476 #endif
5477
5478 int el_act2img(int element, int action)
5479 {
5480   element = GFX_ELEMENT(element);
5481
5482   return element_info[element].graphic[action];
5483 }
5484
5485 int el_act2crm(int element, int action)
5486 {
5487   element = GFX_ELEMENT(element);
5488
5489   return element_info[element].crumbled[action];
5490 }
5491
5492 int el_dir2img(int element, int direction)
5493 {
5494   element = GFX_ELEMENT(element);
5495
5496   return el_act_dir2img(element, ACTION_DEFAULT, direction);
5497 }
5498
5499 int el2baseimg(int element)
5500 {
5501   return element_info[element].graphic[ACTION_DEFAULT];
5502 }
5503
5504 int el2img(int element)
5505 {
5506   element = GFX_ELEMENT(element);
5507
5508   return element_info[element].graphic[ACTION_DEFAULT];
5509 }
5510
5511 int el2edimg(int element)
5512 {
5513   element = GFX_ELEMENT(element);
5514
5515   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
5516 }
5517
5518 int el2preimg(int element)
5519 {
5520   element = GFX_ELEMENT(element);
5521
5522   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
5523 }
5524
5525 int font2baseimg(int font_nr)
5526 {
5527   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
5528 }
5529
5530 int getNumActivePlayers_EM()
5531 {
5532   int num_players = 0;
5533   int i;
5534
5535   if (!tape.playing)
5536     return -1;
5537
5538   for (i = 0; i < MAX_PLAYERS; i++)
5539     if (tape.player_participates[i])
5540       num_players++;
5541
5542   return num_players;
5543 }
5544
5545 int getGameFrameDelay_EM(int native_em_game_frame_delay)
5546 {
5547   int game_frame_delay_value;
5548
5549   game_frame_delay_value =
5550     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
5551      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
5552      GameFrameDelay);
5553
5554   if (tape.playing && tape.warp_forward && !tape.pausing)
5555     game_frame_delay_value = 0;
5556
5557   return game_frame_delay_value;
5558 }
5559
5560 unsigned int InitRND(long seed)
5561 {
5562   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
5563     return InitEngineRandom_EM(seed);
5564   else
5565     return InitEngineRandom_RND(seed);
5566 }
5567
5568 void InitGraphicInfo_EM(void)
5569 {
5570   struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
5571   struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
5572   int i, j, p;
5573
5574 #if DEBUG_EM_GFX
5575   int num_em_gfx_errors = 0;
5576
5577   if (graphic_info_em_object[0][0].bitmap == NULL)
5578   {
5579     /* EM graphics not yet initialized in em_open_all() */
5580
5581     return;
5582   }
5583
5584   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
5585 #endif
5586
5587   /* always start with reliable default values */
5588   for (i = 0; i < TILE_MAX; i++)
5589   {
5590     object_mapping[i].element_rnd = EL_UNKNOWN;
5591     object_mapping[i].is_backside = FALSE;
5592     object_mapping[i].action = ACTION_DEFAULT;
5593     object_mapping[i].direction = MV_NONE;
5594   }
5595
5596   /* always start with reliable default values */
5597   for (p = 0; p < MAX_PLAYERS; p++)
5598   {
5599     for (i = 0; i < SPR_MAX; i++)
5600     {
5601       player_mapping[p][i].element_rnd = EL_UNKNOWN;
5602       player_mapping[p][i].action = ACTION_DEFAULT;
5603       player_mapping[p][i].direction = MV_NONE;
5604     }
5605   }
5606
5607   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
5608   {
5609     int e = em_object_mapping_list[i].element_em;
5610
5611     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
5612     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
5613
5614     if (em_object_mapping_list[i].action != -1)
5615       object_mapping[e].action = em_object_mapping_list[i].action;
5616
5617     if (em_object_mapping_list[i].direction != -1)
5618       object_mapping[e].direction =
5619         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
5620   }
5621
5622   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
5623   {
5624     int a = em_player_mapping_list[i].action_em;
5625     int p = em_player_mapping_list[i].player_nr;
5626
5627     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
5628
5629     if (em_player_mapping_list[i].action != -1)
5630       player_mapping[p][a].action = em_player_mapping_list[i].action;
5631
5632     if (em_player_mapping_list[i].direction != -1)
5633       player_mapping[p][a].direction =
5634         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
5635   }
5636
5637   for (i = 0; i < TILE_MAX; i++)
5638   {
5639     int element = object_mapping[i].element_rnd;
5640     int action = object_mapping[i].action;
5641     int direction = object_mapping[i].direction;
5642     boolean is_backside = object_mapping[i].is_backside;
5643     boolean action_removing = (action == ACTION_DIGGING ||
5644                                action == ACTION_SNAPPING ||
5645                                action == ACTION_COLLECTING);
5646     boolean action_exploding = ((action == ACTION_EXPLODING ||
5647                                  action == ACTION_SMASHED_BY_ROCK ||
5648                                  action == ACTION_SMASHED_BY_SPRING) &&
5649                                 element != EL_DIAMOND);
5650     boolean action_active = (action == ACTION_ACTIVE);
5651     boolean action_other = (action == ACTION_OTHER);
5652
5653     for (j = 0; j < 8; j++)
5654     {
5655       int effective_element = (j > 5 && i == Yacid_splash_eB ? EL_EMPTY :
5656                                j > 5 && i == Yacid_splash_wB ? EL_EMPTY :
5657                                j < 7 ? element :
5658                                i == Xdrip_stretch ? element :
5659                                i == Xdrip_stretchB ? element :
5660                                i == Ydrip_s1 ? element :
5661                                i == Ydrip_s1B ? element :
5662                                i == Xball_1B ? element :
5663                                i == Xball_2 ? element :
5664                                i == Xball_2B ? element :
5665                                i == Yball_eat ? element :
5666                                i == Ykey_1_eat ? element :
5667                                i == Ykey_2_eat ? element :
5668                                i == Ykey_3_eat ? element :
5669                                i == Ykey_4_eat ? element :
5670                                i == Ykey_5_eat ? element :
5671                                i == Ykey_6_eat ? element :
5672                                i == Ykey_7_eat ? element :
5673                                i == Ykey_8_eat ? element :
5674                                i == Ylenses_eat ? element :
5675                                i == Ymagnify_eat ? element :
5676                                i == Ygrass_eat ? element :
5677                                i == Ydirt_eat ? element :
5678                                i == Yemerald_stone ? EL_EMERALD :
5679                                i == Ydiamond_stone ? EL_ROCK :
5680                                i == Xsand_stonein_1 ? element :
5681                                i == Xsand_stonein_2 ? element :
5682                                i == Xsand_stonein_3 ? element :
5683                                i == Xsand_stonein_4 ? element :
5684                                is_backside ? EL_EMPTY :
5685                                action_removing ? EL_EMPTY :
5686                                element);
5687       int effective_action = (j < 7 ? action :
5688                               i == Xdrip_stretch ? action :
5689                               i == Xdrip_stretchB ? action :
5690                               i == Ydrip_s1 ? action :
5691                               i == Ydrip_s1B ? action :
5692                               i == Xball_1B ? action :
5693                               i == Xball_2 ? action :
5694                               i == Xball_2B ? action :
5695                               i == Yball_eat ? action :
5696                               i == Ykey_1_eat ? action :
5697                               i == Ykey_2_eat ? action :
5698                               i == Ykey_3_eat ? action :
5699                               i == Ykey_4_eat ? action :
5700                               i == Ykey_5_eat ? action :
5701                               i == Ykey_6_eat ? action :
5702                               i == Ykey_7_eat ? action :
5703                               i == Ykey_8_eat ? action :
5704                               i == Ylenses_eat ? action :
5705                               i == Ymagnify_eat ? action :
5706                               i == Ygrass_eat ? action :
5707                               i == Ydirt_eat ? action :
5708                               i == Xsand_stonein_1 ? action :
5709                               i == Xsand_stonein_2 ? action :
5710                               i == Xsand_stonein_3 ? action :
5711                               i == Xsand_stonein_4 ? action :
5712                               i == Xsand_stoneout_1 ? action :
5713                               i == Xsand_stoneout_2 ? action :
5714                               i == Xboom_android ? ACTION_EXPLODING :
5715                               action_exploding ? ACTION_EXPLODING :
5716                               action_active ? action :
5717                               action_other ? action :
5718                               ACTION_DEFAULT);
5719       int graphic = (el_act_dir2img(effective_element, effective_action,
5720                                     direction));
5721       int crumbled = (el_act_dir2crm(effective_element, effective_action,
5722                                      direction));
5723       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
5724       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
5725       boolean has_action_graphics = (graphic != base_graphic);
5726       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
5727       struct GraphicInfo *g = &graphic_info[graphic];
5728       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
5729       Bitmap *src_bitmap;
5730       int src_x, src_y;
5731       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
5732       boolean special_animation = (action != ACTION_DEFAULT &&
5733                                    g->anim_frames == 3 &&
5734                                    g->anim_delay == 2 &&
5735                                    g->anim_mode & ANIM_LINEAR);
5736       int sync_frame = (i == Xdrip_stretch ? 7 :
5737                         i == Xdrip_stretchB ? 7 :
5738                         i == Ydrip_s2 ? j + 8 :
5739                         i == Ydrip_s2B ? j + 8 :
5740                         i == Xacid_1 ? 0 :
5741                         i == Xacid_2 ? 10 :
5742                         i == Xacid_3 ? 20 :
5743                         i == Xacid_4 ? 30 :
5744                         i == Xacid_5 ? 40 :
5745                         i == Xacid_6 ? 50 :
5746                         i == Xacid_7 ? 60 :
5747                         i == Xacid_8 ? 70 :
5748                         i == Xfake_acid_1 ? 0 :
5749                         i == Xfake_acid_2 ? 10 :
5750                         i == Xfake_acid_3 ? 20 :
5751                         i == Xfake_acid_4 ? 30 :
5752                         i == Xfake_acid_5 ? 40 :
5753                         i == Xfake_acid_6 ? 50 :
5754                         i == Xfake_acid_7 ? 60 :
5755                         i == Xfake_acid_8 ? 70 :
5756                         i == Xball_2 ? 7 :
5757                         i == Xball_2B ? j + 8 :
5758                         i == Yball_eat ? j + 1 :
5759                         i == Ykey_1_eat ? j + 1 :
5760                         i == Ykey_2_eat ? j + 1 :
5761                         i == Ykey_3_eat ? j + 1 :
5762                         i == Ykey_4_eat ? j + 1 :
5763                         i == Ykey_5_eat ? j + 1 :
5764                         i == Ykey_6_eat ? j + 1 :
5765                         i == Ykey_7_eat ? j + 1 :
5766                         i == Ykey_8_eat ? j + 1 :
5767                         i == Ylenses_eat ? j + 1 :
5768                         i == Ymagnify_eat ? j + 1 :
5769                         i == Ygrass_eat ? j + 1 :
5770                         i == Ydirt_eat ? j + 1 :
5771                         i == Xamoeba_1 ? 0 :
5772                         i == Xamoeba_2 ? 1 :
5773                         i == Xamoeba_3 ? 2 :
5774                         i == Xamoeba_4 ? 3 :
5775                         i == Xamoeba_5 ? 0 :
5776                         i == Xamoeba_6 ? 1 :
5777                         i == Xamoeba_7 ? 2 :
5778                         i == Xamoeba_8 ? 3 :
5779                         i == Xexit_2 ? j + 8 :
5780                         i == Xexit_3 ? j + 16 :
5781                         i == Xdynamite_1 ? 0 :
5782                         i == Xdynamite_2 ? 8 :
5783                         i == Xdynamite_3 ? 16 :
5784                         i == Xdynamite_4 ? 24 :
5785                         i == Xsand_stonein_1 ? j + 1 :
5786                         i == Xsand_stonein_2 ? j + 9 :
5787                         i == Xsand_stonein_3 ? j + 17 :
5788                         i == Xsand_stonein_4 ? j + 25 :
5789                         i == Xsand_stoneout_1 && j == 0 ? 0 :
5790                         i == Xsand_stoneout_1 && j == 1 ? 0 :
5791                         i == Xsand_stoneout_1 && j == 2 ? 1 :
5792                         i == Xsand_stoneout_1 && j == 3 ? 2 :
5793                         i == Xsand_stoneout_1 && j == 4 ? 2 :
5794                         i == Xsand_stoneout_1 && j == 5 ? 3 :
5795                         i == Xsand_stoneout_1 && j == 6 ? 4 :
5796                         i == Xsand_stoneout_1 && j == 7 ? 4 :
5797                         i == Xsand_stoneout_2 && j == 0 ? 5 :
5798                         i == Xsand_stoneout_2 && j == 1 ? 6 :
5799                         i == Xsand_stoneout_2 && j == 2 ? 7 :
5800                         i == Xsand_stoneout_2 && j == 3 ? 8 :
5801                         i == Xsand_stoneout_2 && j == 4 ? 9 :
5802                         i == Xsand_stoneout_2 && j == 5 ? 11 :
5803                         i == Xsand_stoneout_2 && j == 6 ? 13 :
5804                         i == Xsand_stoneout_2 && j == 7 ? 15 :
5805                         i == Xboom_bug && j == 1 ? 2 :
5806                         i == Xboom_bug && j == 2 ? 2 :
5807                         i == Xboom_bug && j == 3 ? 4 :
5808                         i == Xboom_bug && j == 4 ? 4 :
5809                         i == Xboom_bug && j == 5 ? 2 :
5810                         i == Xboom_bug && j == 6 ? 2 :
5811                         i == Xboom_bug && j == 7 ? 0 :
5812                         i == Xboom_bomb && j == 1 ? 2 :
5813                         i == Xboom_bomb && j == 2 ? 2 :
5814                         i == Xboom_bomb && j == 3 ? 4 :
5815                         i == Xboom_bomb && j == 4 ? 4 :
5816                         i == Xboom_bomb && j == 5 ? 2 :
5817                         i == Xboom_bomb && j == 6 ? 2 :
5818                         i == Xboom_bomb && j == 7 ? 0 :
5819                         i == Xboom_android && j == 7 ? 6 :
5820                         i == Xboom_1 && j == 1 ? 2 :
5821                         i == Xboom_1 && j == 2 ? 2 :
5822                         i == Xboom_1 && j == 3 ? 4 :
5823                         i == Xboom_1 && j == 4 ? 4 :
5824                         i == Xboom_1 && j == 5 ? 6 :
5825                         i == Xboom_1 && j == 6 ? 6 :
5826                         i == Xboom_1 && j == 7 ? 8 :
5827                         i == Xboom_2 && j == 0 ? 8 :
5828                         i == Xboom_2 && j == 1 ? 8 :
5829                         i == Xboom_2 && j == 2 ? 10 :
5830                         i == Xboom_2 && j == 3 ? 10 :
5831                         i == Xboom_2 && j == 4 ? 10 :
5832                         i == Xboom_2 && j == 5 ? 12 :
5833                         i == Xboom_2 && j == 6 ? 12 :
5834                         i == Xboom_2 && j == 7 ? 12 :
5835                         special_animation && j == 4 ? 3 :
5836                         effective_action != action ? 0 :
5837                         j);
5838
5839 #if DEBUG_EM_GFX
5840       Bitmap *debug_bitmap = g_em->bitmap;
5841       int debug_src_x = g_em->src_x;
5842       int debug_src_y = g_em->src_y;
5843 #endif
5844
5845       int frame = getAnimationFrame(g->anim_frames,
5846                                     g->anim_delay,
5847                                     g->anim_mode,
5848                                     g->anim_start_frame,
5849                                     sync_frame);
5850
5851       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
5852                           g->double_movement && is_backside);
5853
5854       g_em->bitmap = src_bitmap;
5855       g_em->src_x = src_x;
5856       g_em->src_y = src_y;
5857       g_em->src_offset_x = 0;
5858       g_em->src_offset_y = 0;
5859       g_em->dst_offset_x = 0;
5860       g_em->dst_offset_y = 0;
5861       g_em->width  = TILEX;
5862       g_em->height = TILEY;
5863
5864       g_em->crumbled_bitmap = NULL;
5865       g_em->crumbled_src_x = 0;
5866       g_em->crumbled_src_y = 0;
5867       g_em->crumbled_border_size = 0;
5868
5869       g_em->has_crumbled_graphics = FALSE;
5870       g_em->preserve_background = FALSE;
5871
5872 #if 0
5873       if (has_crumbled_graphics && crumbled == IMG_EMPTY_SPACE)
5874         printf("::: empty crumbled: %d [%s], %d, %d\n",
5875                effective_element, element_info[effective_element].token_name,
5876                effective_action, direction);
5877 #endif
5878
5879       /* if element can be crumbled, but certain action graphics are just empty
5880          space (like snapping sand with the original R'n'D graphics), do not
5881          treat these empty space graphics as crumbled graphics in EMC engine */
5882       if (has_crumbled_graphics && crumbled != IMG_EMPTY_SPACE)
5883       {
5884         getGraphicSource(crumbled, frame, &src_bitmap, &src_x, &src_y);
5885
5886         g_em->has_crumbled_graphics = TRUE;
5887         g_em->crumbled_bitmap = src_bitmap;
5888         g_em->crumbled_src_x = src_x;
5889         g_em->crumbled_src_y = src_y;
5890         g_em->crumbled_border_size = graphic_info[crumbled].border_size;
5891       }
5892
5893 #if 0
5894       if (element == EL_ROCK &&
5895           effective_action == ACTION_FILLING)
5896         printf("::: has_action_graphics == %d\n", has_action_graphics);
5897 #endif
5898
5899       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
5900                                    effective_action == ACTION_MOVING  ||
5901                                    effective_action == ACTION_PUSHING ||
5902                                    effective_action == ACTION_EATING)) ||
5903           (!has_action_graphics && (effective_action == ACTION_FILLING ||
5904                                     effective_action == ACTION_EMPTYING)))
5905       {
5906         int move_dir =
5907           (effective_action == ACTION_FALLING ||
5908            effective_action == ACTION_FILLING ||
5909            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
5910         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
5911         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
5912         int num_steps = (i == Ydrip_s1  ? 16 :
5913                          i == Ydrip_s1B ? 16 :
5914                          i == Ydrip_s2  ? 16 :
5915                          i == Ydrip_s2B ? 16 :
5916                          i == Xsand_stonein_1 ? 32 :
5917                          i == Xsand_stonein_2 ? 32 :
5918                          i == Xsand_stonein_3 ? 32 :
5919                          i == Xsand_stonein_4 ? 32 :
5920                          i == Xsand_stoneout_1 ? 16 :
5921                          i == Xsand_stoneout_2 ? 16 : 8);
5922         int cx = ABS(dx) * (TILEX / num_steps);
5923         int cy = ABS(dy) * (TILEY / num_steps);
5924         int step_frame = (i == Ydrip_s2         ? j + 8 :
5925                           i == Ydrip_s2B        ? j + 8 :
5926                           i == Xsand_stonein_2  ? j + 8 :
5927                           i == Xsand_stonein_3  ? j + 16 :
5928                           i == Xsand_stonein_4  ? j + 24 :
5929                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
5930         int step = (is_backside ? step_frame : num_steps - step_frame);
5931
5932         if (is_backside)        /* tile where movement starts */
5933         {
5934           if (dx < 0 || dy < 0)
5935           {
5936             g_em->src_offset_x = cx * step;
5937             g_em->src_offset_y = cy * step;
5938           }
5939           else
5940           {
5941             g_em->dst_offset_x = cx * step;
5942             g_em->dst_offset_y = cy * step;
5943           }
5944         }
5945         else                    /* tile where movement ends */
5946         {
5947           if (dx < 0 || dy < 0)
5948           {
5949             g_em->dst_offset_x = cx * step;
5950             g_em->dst_offset_y = cy * step;
5951           }
5952           else
5953           {
5954             g_em->src_offset_x = cx * step;
5955             g_em->src_offset_y = cy * step;
5956           }
5957         }
5958
5959         g_em->width  = TILEX - cx * step;
5960         g_em->height = TILEY - cy * step;
5961       }
5962
5963       /* create unique graphic identifier to decide if tile must be redrawn */
5964       /* bit 31 - 16 (16 bit): EM style graphic
5965          bit 15 - 12 ( 4 bit): EM style frame
5966          bit 11 -  6 ( 6 bit): graphic width
5967          bit  5 -  0 ( 6 bit): graphic height */
5968       g_em->unique_identifier =
5969         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
5970
5971 #if DEBUG_EM_GFX
5972
5973       /* skip check for EMC elements not contained in original EMC artwork */
5974       if (element == EL_EMC_FAKE_ACID)
5975         continue;
5976
5977       if (g_em->bitmap != debug_bitmap ||
5978           g_em->src_x != debug_src_x ||
5979           g_em->src_y != debug_src_y ||
5980           g_em->src_offset_x != 0 ||
5981           g_em->src_offset_y != 0 ||
5982           g_em->dst_offset_x != 0 ||
5983           g_em->dst_offset_y != 0 ||
5984           g_em->width != TILEX ||
5985           g_em->height != TILEY)
5986       {
5987         static int last_i = -1;
5988
5989         if (i != last_i)
5990         {
5991           printf("\n");
5992           last_i = i;
5993         }
5994
5995         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
5996                i, element, element_info[element].token_name,
5997                element_action_info[effective_action].suffix, direction);
5998
5999         if (element != effective_element)
6000           printf(" [%d ('%s')]",
6001                  effective_element,
6002                  element_info[effective_element].token_name);
6003
6004         printf("\n");
6005
6006         if (g_em->bitmap != debug_bitmap)
6007           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
6008                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
6009
6010         if (g_em->src_x != debug_src_x ||
6011             g_em->src_y != debug_src_y)
6012           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
6013                  j, (is_backside ? 'B' : 'F'),
6014                  g_em->src_x, g_em->src_y,
6015                  g_em->src_x / 32, g_em->src_y / 32,
6016                  debug_src_x, debug_src_y,
6017                  debug_src_x / 32, debug_src_y / 32);
6018
6019         if (g_em->src_offset_x != 0 ||
6020             g_em->src_offset_y != 0 ||
6021             g_em->dst_offset_x != 0 ||
6022             g_em->dst_offset_y != 0)
6023           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
6024                  j, is_backside,
6025                  g_em->src_offset_x, g_em->src_offset_y,
6026                  g_em->dst_offset_x, g_em->dst_offset_y);
6027
6028         if (g_em->width != TILEX ||
6029             g_em->height != TILEY)
6030           printf("    %d (%d): size %d,%d should be %d,%d\n",
6031                  j, is_backside,
6032                  g_em->width, g_em->height, TILEX, TILEY);
6033
6034         num_em_gfx_errors++;
6035       }
6036 #endif
6037
6038     }
6039   }
6040
6041   for (i = 0; i < TILE_MAX; i++)
6042   {
6043     for (j = 0; j < 8; j++)
6044     {
6045       int element = object_mapping[i].element_rnd;
6046       int action = object_mapping[i].action;
6047       int direction = object_mapping[i].direction;
6048       boolean is_backside = object_mapping[i].is_backside;
6049       int graphic_action  = el_act_dir2img(element, action, direction);
6050       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
6051
6052       if ((action == ACTION_SMASHED_BY_ROCK ||
6053            action == ACTION_SMASHED_BY_SPRING ||
6054            action == ACTION_EATING) &&
6055           graphic_action == graphic_default)
6056       {
6057         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
6058                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
6059                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
6060                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
6061                  Xspring);
6062
6063         /* no separate animation for "smashed by rock" -- use rock instead */
6064         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
6065         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
6066
6067         g_em->bitmap            = g_xx->bitmap;
6068         g_em->src_x             = g_xx->src_x;
6069         g_em->src_y             = g_xx->src_y;
6070         g_em->src_offset_x      = g_xx->src_offset_x;
6071         g_em->src_offset_y      = g_xx->src_offset_y;
6072         g_em->dst_offset_x      = g_xx->dst_offset_x;
6073         g_em->dst_offset_y      = g_xx->dst_offset_y;
6074         g_em->width             = g_xx->width;
6075         g_em->height            = g_xx->height;
6076         g_em->unique_identifier = g_xx->unique_identifier;
6077
6078         if (!is_backside)
6079           g_em->preserve_background = TRUE;
6080       }
6081     }
6082   }
6083
6084   for (p = 0; p < MAX_PLAYERS; p++)
6085   {
6086     for (i = 0; i < SPR_MAX; i++)
6087     {
6088       int element = player_mapping[p][i].element_rnd;
6089       int action = player_mapping[p][i].action;
6090       int direction = player_mapping[p][i].direction;
6091
6092       for (j = 0; j < 8; j++)
6093       {
6094         int effective_element = element;
6095         int effective_action = action;
6096         int graphic = (direction == MV_NONE ?
6097                        el_act2img(effective_element, effective_action) :
6098                        el_act_dir2img(effective_element, effective_action,
6099                                       direction));
6100         struct GraphicInfo *g = &graphic_info[graphic];
6101         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
6102         Bitmap *src_bitmap;
6103         int src_x, src_y;
6104         int sync_frame = j;
6105
6106 #if DEBUG_EM_GFX
6107         Bitmap *debug_bitmap = g_em->bitmap;
6108         int debug_src_x = g_em->src_x;
6109         int debug_src_y = g_em->src_y;
6110 #endif
6111
6112         int frame = getAnimationFrame(g->anim_frames,
6113                                       g->anim_delay,
6114                                       g->anim_mode,
6115                                       g->anim_start_frame,
6116                                       sync_frame);
6117
6118         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x,&src_y, FALSE);
6119
6120         g_em->bitmap = src_bitmap;
6121         g_em->src_x = src_x;
6122         g_em->src_y = src_y;
6123         g_em->src_offset_x = 0;
6124         g_em->src_offset_y = 0;
6125         g_em->dst_offset_x = 0;
6126         g_em->dst_offset_y = 0;
6127         g_em->width  = TILEX;
6128         g_em->height = TILEY;
6129
6130 #if DEBUG_EM_GFX
6131
6132         /* skip check for EMC elements not contained in original EMC artwork */
6133         if (element == EL_PLAYER_3 ||
6134             element == EL_PLAYER_4)
6135           continue;
6136
6137         if (g_em->bitmap != debug_bitmap ||
6138             g_em->src_x != debug_src_x ||
6139             g_em->src_y != debug_src_y)
6140         {
6141           static int last_i = -1;
6142
6143           if (i != last_i)
6144           {
6145             printf("\n");
6146             last_i = i;
6147           }
6148
6149           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
6150                  p, i, element, element_info[element].token_name,
6151                  element_action_info[effective_action].suffix, direction);
6152
6153           if (element != effective_element)
6154             printf(" [%d ('%s')]",
6155                    effective_element,
6156                    element_info[effective_element].token_name);
6157
6158           printf("\n");
6159
6160           if (g_em->bitmap != debug_bitmap)
6161             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
6162                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
6163
6164           if (g_em->src_x != debug_src_x ||
6165               g_em->src_y != debug_src_y)
6166             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
6167                    j,
6168                    g_em->src_x, g_em->src_y,
6169                    g_em->src_x / 32, g_em->src_y / 32,
6170                    debug_src_x, debug_src_y,
6171                    debug_src_x / 32, debug_src_y / 32);
6172
6173           num_em_gfx_errors++;
6174         }
6175 #endif
6176
6177       }
6178     }
6179   }
6180
6181 #if DEBUG_EM_GFX
6182   printf("\n");
6183   printf("::: [%d errors found]\n", num_em_gfx_errors);
6184
6185   exit(0);
6186 #endif
6187 }
6188
6189 void PlayMenuSound()
6190 {
6191   int sound = menu.sound[game_status];
6192
6193   if (sound == SND_UNDEFINED)
6194     return;
6195
6196   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
6197       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
6198     return;
6199
6200   if (IS_LOOP_SOUND(sound))
6201     PlaySoundLoop(sound);
6202   else
6203     PlaySound(sound);
6204 }
6205
6206 void PlayMenuSoundStereo(int sound, int stereo_position)
6207 {
6208   if (sound == SND_UNDEFINED)
6209     return;
6210
6211   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
6212       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
6213     return;
6214
6215   if (IS_LOOP_SOUND(sound))
6216     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
6217   else
6218     PlaySoundStereo(sound, stereo_position);
6219 }
6220
6221 void PlayMenuSoundIfLoop()
6222 {
6223   int sound = menu.sound[game_status];
6224
6225   if (sound == SND_UNDEFINED)
6226     return;
6227
6228   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
6229       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
6230     return;
6231
6232   if (IS_LOOP_SOUND(sound))
6233     PlaySoundLoop(sound);
6234 }
6235
6236 void PlayMenuMusic()
6237 {
6238   int music = menu.music[game_status];
6239
6240   if (music == MUS_UNDEFINED)
6241     return;
6242
6243   if (!setup.sound_music)
6244     return;
6245
6246   PlayMusic(music);
6247 }
6248
6249 void PlaySoundActivating()
6250 {
6251 #if 0
6252   PlaySound(SND_MENU_ITEM_ACTIVATING);
6253 #endif
6254 }
6255
6256 void PlaySoundSelecting()
6257 {
6258 #if 0
6259   PlaySound(SND_MENU_ITEM_SELECTING);
6260 #endif
6261 }
6262
6263 void ToggleFullscreenIfNeeded()
6264 {
6265   boolean change_fullscreen = (setup.fullscreen !=
6266                                video.fullscreen_enabled);
6267   boolean change_fullscreen_mode = (video.fullscreen_enabled &&
6268                                     !strEqual(setup.fullscreen_mode,
6269                                               video.fullscreen_mode_current));
6270
6271   if (!video.fullscreen_available)
6272     return;
6273
6274 #if 1
6275   if (change_fullscreen || change_fullscreen_mode)
6276 #else
6277   if (setup.fullscreen != video.fullscreen_enabled ||
6278       setup.fullscreen_mode != video.fullscreen_mode_current)
6279 #endif
6280   {
6281     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
6282
6283     /* save backbuffer content which gets lost when toggling fullscreen mode */
6284     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
6285
6286 #if 1
6287     if (change_fullscreen_mode)
6288 #else
6289     if (setup.fullscreen && video.fullscreen_enabled)
6290 #endif
6291     {
6292       /* keep fullscreen, but change fullscreen mode (screen resolution) */
6293 #if 1
6294       /* (this is now set in sdl.c) */
6295 #else
6296       video.fullscreen_mode_current = setup.fullscreen_mode;
6297 #endif
6298       video.fullscreen_enabled = FALSE;         /* force new fullscreen mode */
6299     }
6300
6301     /* toggle fullscreen */
6302     ChangeVideoModeIfNeeded(setup.fullscreen);
6303
6304     setup.fullscreen = video.fullscreen_enabled;
6305
6306     /* restore backbuffer content from temporary backbuffer backup bitmap */
6307     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
6308
6309     FreeBitmap(tmp_backbuffer);
6310
6311 #if 1
6312     /* update visible window/screen */
6313     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
6314 #else
6315     redraw_mask = REDRAW_ALL;
6316 #endif
6317   }
6318 }