rnd-20070121-2-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       font_nr = FONT_LEVEL_NUMBER;
2527
2528       break;
2529     }
2530   }
2531
2532   if (game_status == GAME_MODE_PLAYING &&
2533       level.game_engine_type == GAME_ENGINE_TYPE_EM)
2534     BlitScreenToBitmap_EM(backbuffer);
2535
2536   /* disable deactivated drawing when quick-loading level tape recording */
2537   if (tape.playing && tape.deactivate_display)
2538     TapeDeactivateDisplayOff(TRUE);
2539
2540   SetMouseCursor(CURSOR_DEFAULT);
2541
2542 #if defined(NETWORK_AVALIABLE)
2543   /* pause network game while waiting for request to answer */
2544   if (options.network &&
2545       game_status == GAME_MODE_PLAYING &&
2546       req_state & REQUEST_WAIT_FOR_INPUT)
2547     SendToServer_PausePlaying();
2548 #endif
2549
2550   old_door_state = GetDoorState();
2551
2552   /* simulate releasing mouse button over last gadget, if still pressed */
2553   if (button_status)
2554     HandleGadgets(-1, -1, 0);
2555
2556   UnmapAllGadgets();
2557
2558   if (old_door_state & DOOR_OPEN_1)
2559   {
2560     CloseDoor(DOOR_CLOSE_1);
2561
2562     /* save old door content */
2563     BlitBitmap(bitmap_db_door, bitmap_db_door,
2564                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2565                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
2566   }
2567
2568 #if 1
2569   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
2570 #endif
2571
2572   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2573
2574   /* clear door drawing field */
2575   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2576
2577   /* force DOOR font on preview level */
2578   game_status = GAME_MODE_PSEUDO_DOOR;
2579
2580   /* write text for request */
2581   for (ty = 0; ty < MAX_REQUEST_LINES; ty++)
2582   {
2583     char text_line[max_request_line_len + 1];
2584     int tx, tl, tc = 0;
2585
2586     if (!*text)
2587       break;
2588
2589     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
2590     {
2591       tc = *(text + tx);
2592       if (!tc || tc == ' ')
2593         break;
2594     }
2595
2596     if (!tl)
2597     { 
2598       text++; 
2599       ty--; 
2600       continue; 
2601     }
2602
2603     strncpy(text_line, text, tl);
2604     text_line[tl] = 0;
2605
2606     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
2607              DY + 8 + ty * (getFontHeight(font_nr) + 2),
2608              text_line, font_nr);
2609
2610     text += tl + (tc == ' ' ? 1 : 0);
2611   }
2612
2613   game_status = last_game_status;       /* restore current game status */
2614
2615   if (req_state & REQ_ASK)
2616   {
2617     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2618     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2619   }
2620   else if (req_state & REQ_CONFIRM)
2621   {
2622     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2623   }
2624   else if (req_state & REQ_PLAYER)
2625   {
2626     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2627     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2628     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2629     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2630   }
2631
2632   /* copy request gadgets to door backbuffer */
2633   BlitBitmap(drawto, bitmap_db_door,
2634              DX, DY, DXSIZE, DYSIZE,
2635              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2636
2637   OpenDoor(DOOR_OPEN_1);
2638
2639   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
2640   {
2641     if (game_status == GAME_MODE_PLAYING)
2642     {
2643       SetPanelBackground();
2644       SetDrawBackgroundMask(REDRAW_DOOR_1);
2645     }
2646     else
2647     {
2648       SetDrawBackgroundMask(REDRAW_FIELD);
2649     }
2650
2651     return FALSE;
2652   }
2653
2654   if (game_status != GAME_MODE_MAIN)
2655     InitAnimation();
2656
2657   button_status = MB_RELEASED;
2658
2659   request_gadget_id = -1;
2660
2661   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2662
2663   while (result < 0)
2664   {
2665     if (PendingEvent())
2666     {
2667       Event event;
2668
2669       NextEvent(&event);
2670
2671       switch (event.type)
2672       {
2673         case EVENT_BUTTONPRESS:
2674         case EVENT_BUTTONRELEASE:
2675         case EVENT_MOTIONNOTIFY:
2676         {
2677           if (event.type == EVENT_MOTIONNOTIFY)
2678           {
2679             if (!PointerInWindow(window))
2680               continue; /* window and pointer are on different screens */
2681
2682             if (!button_status)
2683               continue;
2684
2685             motion_status = TRUE;
2686             mx = ((MotionEvent *) &event)->x;
2687             my = ((MotionEvent *) &event)->y;
2688           }
2689           else
2690           {
2691             motion_status = FALSE;
2692             mx = ((ButtonEvent *) &event)->x;
2693             my = ((ButtonEvent *) &event)->y;
2694             if (event.type == EVENT_BUTTONPRESS)
2695               button_status = ((ButtonEvent *) &event)->button;
2696             else
2697               button_status = MB_RELEASED;
2698           }
2699
2700           /* this sets 'request_gadget_id' */
2701           HandleGadgets(mx, my, button_status);
2702
2703           switch (request_gadget_id)
2704           {
2705             case TOOL_CTRL_ID_YES:
2706               result = TRUE;
2707               break;
2708             case TOOL_CTRL_ID_NO:
2709               result = FALSE;
2710               break;
2711             case TOOL_CTRL_ID_CONFIRM:
2712               result = TRUE | FALSE;
2713               break;
2714
2715             case TOOL_CTRL_ID_PLAYER_1:
2716               result = 1;
2717               break;
2718             case TOOL_CTRL_ID_PLAYER_2:
2719               result = 2;
2720               break;
2721             case TOOL_CTRL_ID_PLAYER_3:
2722               result = 3;
2723               break;
2724             case TOOL_CTRL_ID_PLAYER_4:
2725               result = 4;
2726               break;
2727
2728             default:
2729               break;
2730           }
2731
2732           break;
2733         }
2734
2735         case EVENT_KEYPRESS:
2736           switch (GetEventKey((KeyEvent *)&event, TRUE))
2737           {
2738             case KSYM_Return:
2739               result = 1;
2740               break;
2741
2742             case KSYM_Escape:
2743               result = 0;
2744               break;
2745
2746             default:
2747               break;
2748           }
2749           if (req_state & REQ_PLAYER)
2750             result = 0;
2751           break;
2752
2753         case EVENT_KEYRELEASE:
2754           ClearPlayerAction();
2755           break;
2756
2757         default:
2758           HandleOtherEvents(&event);
2759           break;
2760       }
2761     }
2762     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2763     {
2764       int joy = AnyJoystick();
2765
2766       if (joy & JOY_BUTTON_1)
2767         result = 1;
2768       else if (joy & JOY_BUTTON_2)
2769         result = 0;
2770     }
2771
2772     DoAnimation();
2773
2774 #if 1
2775     if (!PendingEvent())        /* delay only if no pending events */
2776       Delay(10);
2777 #else
2778     /* don't eat all CPU time */
2779     Delay(10);
2780 #endif
2781   }
2782
2783   if (game_status != GAME_MODE_MAIN)
2784     StopAnimation();
2785
2786   UnmapToolButtons();
2787
2788   if (!(req_state & REQ_STAY_OPEN))
2789   {
2790     CloseDoor(DOOR_CLOSE_1);
2791
2792     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
2793         (req_state & REQ_REOPEN))
2794       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
2795   }
2796
2797   RemapAllGadgets();
2798
2799   if (game_status == GAME_MODE_PLAYING)
2800   {
2801     SetPanelBackground();
2802     SetDrawBackgroundMask(REDRAW_DOOR_1);
2803   }
2804   else
2805   {
2806     SetDrawBackgroundMask(REDRAW_FIELD);
2807   }
2808
2809 #if defined(NETWORK_AVALIABLE)
2810   /* continue network game after request */
2811   if (options.network &&
2812       game_status == GAME_MODE_PLAYING &&
2813       req_state & REQUEST_WAIT_FOR_INPUT)
2814     SendToServer_ContinuePlaying();
2815 #endif
2816
2817   /* restore deactivated drawing when quick-loading level tape recording */
2818   if (tape.playing && tape.deactivate_display)
2819     TapeDeactivateDisplayOn();
2820
2821   return result;
2822 }
2823
2824 unsigned int OpenDoor(unsigned int door_state)
2825 {
2826   if (door_state & DOOR_COPY_BACK)
2827   {
2828     if (door_state & DOOR_OPEN_1)
2829       BlitBitmap(bitmap_db_door, bitmap_db_door,
2830                  DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2831                  DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2832
2833     if (door_state & DOOR_OPEN_2)
2834       BlitBitmap(bitmap_db_door, bitmap_db_door,
2835                  DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY2, VXSIZE, VYSIZE,
2836                  DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2837
2838     door_state &= ~DOOR_COPY_BACK;
2839   }
2840
2841   return MoveDoor(door_state);
2842 }
2843
2844 unsigned int CloseDoor(unsigned int door_state)
2845 {
2846   unsigned int old_door_state = GetDoorState();
2847
2848   if (!(door_state & DOOR_NO_COPY_BACK))
2849   {
2850     if (old_door_state & DOOR_OPEN_1)
2851       BlitBitmap(backbuffer, bitmap_db_door,
2852                  DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2853
2854     if (old_door_state & DOOR_OPEN_2)
2855       BlitBitmap(backbuffer, bitmap_db_door,
2856                  VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2857
2858     door_state &= ~DOOR_NO_COPY_BACK;
2859   }
2860
2861   return MoveDoor(door_state);
2862 }
2863
2864 unsigned int GetDoorState()
2865 {
2866   return MoveDoor(DOOR_GET_STATE);
2867 }
2868
2869 unsigned int SetDoorState(unsigned int door_state)
2870 {
2871   return MoveDoor(door_state | DOOR_SET_STATE);
2872 }
2873
2874 unsigned int MoveDoor(unsigned int door_state)
2875 {
2876   static int door1 = DOOR_OPEN_1;
2877   static int door2 = DOOR_CLOSE_2;
2878   unsigned long door_delay = 0;
2879   unsigned long door_delay_value;
2880   int stepsize = 1;
2881
2882   if (door_1.width < 0 || door_1.width > DXSIZE)
2883     door_1.width = DXSIZE;
2884   if (door_1.height < 0 || door_1.height > DYSIZE)
2885     door_1.height = DYSIZE;
2886   if (door_2.width < 0 || door_2.width > VXSIZE)
2887     door_2.width = VXSIZE;
2888   if (door_2.height < 0 || door_2.height > VYSIZE)
2889     door_2.height = VYSIZE;
2890
2891   if (door_state == DOOR_GET_STATE)
2892     return (door1 | door2);
2893
2894   if (door_state & DOOR_SET_STATE)
2895   {
2896     if (door_state & DOOR_ACTION_1)
2897       door1 = door_state & DOOR_ACTION_1;
2898     if (door_state & DOOR_ACTION_2)
2899       door2 = door_state & DOOR_ACTION_2;
2900
2901     return (door1 | door2);
2902   }
2903
2904   if (!(door_state & DOOR_FORCE_REDRAW))
2905   {
2906     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2907       door_state &= ~DOOR_OPEN_1;
2908     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2909       door_state &= ~DOOR_CLOSE_1;
2910     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2911       door_state &= ~DOOR_OPEN_2;
2912     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2913       door_state &= ~DOOR_CLOSE_2;
2914   }
2915
2916   door_delay_value = (door_state & DOOR_ACTION_1 ? door_1.step_delay :
2917                       door_2.step_delay);
2918
2919   if (setup.quick_doors)
2920   {
2921     stepsize = 20;              /* must be choosen to always draw last frame */
2922     door_delay_value = 0;
2923   }
2924
2925   if (global.autoplay_leveldir)
2926   {
2927     door_state |= DOOR_NO_DELAY;
2928     door_state &= ~DOOR_CLOSE_ALL;
2929   }
2930
2931   if (door_state & DOOR_ACTION)
2932   {
2933     boolean handle_door_1 = (door_state & DOOR_ACTION_1);
2934     boolean handle_door_2 = (door_state & DOOR_ACTION_2);
2935     boolean door_1_done = (!handle_door_1);
2936     boolean door_2_done = (!handle_door_2);
2937     boolean door_1_vertical = (door_1.anim_mode & ANIM_VERTICAL);
2938     boolean door_2_vertical = (door_2.anim_mode & ANIM_VERTICAL);
2939     int door_size_1 = (door_1_vertical ? door_1.height : door_1.width);
2940     int door_size_2 = (door_2_vertical ? door_2.height : door_2.width);
2941     int max_door_size_1 = (door_1_vertical ? DYSIZE : DXSIZE);
2942     int max_door_size_2 = (door_2_vertical ? VYSIZE : VXSIZE);
2943     int door_size     = (handle_door_1 ? door_size_1     : door_size_2);
2944     int max_door_size = (handle_door_1 ? max_door_size_1 : max_door_size_2);
2945     int door_skip = max_door_size - door_size;
2946     int end = door_size;
2947     int start = ((door_state & DOOR_NO_DELAY) ? end : 0);
2948     int k;
2949
2950     if (!(door_state & DOOR_NO_DELAY) && !setup.quick_doors)
2951     {
2952       /* opening door sound has priority over simultaneously closing door */
2953       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2954         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2955       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2956         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2957     }
2958
2959     for (k = start; k <= end && !(door_1_done && door_2_done); k += stepsize)
2960     {
2961       int x = k;
2962       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2963       GC gc = bitmap->stored_clip_gc;
2964
2965       if (door_state & DOOR_ACTION_1)
2966       {
2967         int a = MIN(x * door_1.step_offset, end);
2968         int p = (door_state & DOOR_OPEN_1 ? end - a : a);
2969         int i = p + door_skip;
2970
2971         if (door_1.anim_mode & ANIM_STATIC_PANEL)
2972         {
2973           BlitBitmap(bitmap_db_door, drawto,
2974                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1,
2975                      DXSIZE, DYSIZE, DX, DY);
2976         }
2977         else if (x <= a)
2978         {
2979           BlitBitmap(bitmap_db_door, drawto,
2980                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + p / 2,
2981                      DXSIZE, DYSIZE - p / 2, DX, DY);
2982
2983           ClearRectangle(drawto, DX, DY + DYSIZE - p / 2, DXSIZE, p / 2);
2984         }
2985
2986         if (door_1.anim_mode & ANIM_HORIZONTAL && x <= DXSIZE)
2987         {
2988           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2989           int dst1_x = DX + DXSIZE - i, dst1_y = DY;
2990           int src2_x = DXSIZE - i,      src2_y = DOOR_GFX_PAGEY1;
2991           int dst2_x = DX,              dst2_y = DY;
2992           int width = i, height = DYSIZE;
2993
2994           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2995           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2996                            dst1_x, dst1_y);
2997
2998           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2999           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
3000                            dst2_x, dst2_y);
3001         }
3002         else if (door_1.anim_mode & ANIM_VERTICAL && x <= DYSIZE)
3003         {
3004           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
3005           int dst1_x = DX,              dst1_y = DY + DYSIZE - i;
3006           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY1 + DYSIZE - i;
3007           int dst2_x = DX,              dst2_y = DY;
3008           int width = DXSIZE, height = i;
3009
3010           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
3011           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
3012                            dst1_x, dst1_y);
3013
3014           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
3015           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
3016                            dst2_x, dst2_y);
3017         }
3018         else if (x <= DXSIZE)   /* ANIM_DEFAULT */
3019         {
3020           int j = (door_1.anim_mode == ANIM_DEFAULT ? (DXSIZE - i) / 3 : 0);
3021
3022           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
3023           BlitBitmapMasked(bitmap, drawto,
3024                            DXSIZE, DOOR_GFX_PAGEY1, i, 77,
3025                            DX + DXSIZE - i, DY + j);
3026           BlitBitmapMasked(bitmap, drawto,
3027                            DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
3028                            DX + DXSIZE - i, DY + 140 + j);
3029           SetClipOrigin(bitmap, gc, DX - DXSIZE + i,
3030                         DY - (DOOR_GFX_PAGEY1 + j));
3031           BlitBitmapMasked(bitmap, drawto,
3032                            DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
3033                            DX, DY);
3034           BlitBitmapMasked(bitmap, drawto,
3035                            DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
3036                            DX, DY + 140 - j);
3037
3038           BlitBitmapMasked(bitmap, drawto,
3039                            DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
3040                            DX, DY + 77 - j);
3041           BlitBitmapMasked(bitmap, drawto,
3042                            DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
3043                            DX, DY + 203 - j);
3044           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
3045           BlitBitmapMasked(bitmap, drawto,
3046                            DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
3047                            DX + DXSIZE - i, DY + 77 + j);
3048           BlitBitmapMasked(bitmap, drawto,
3049                            DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
3050                            DX + DXSIZE - i, DY + 203 + j);
3051         }
3052
3053         redraw_mask |= REDRAW_DOOR_1;
3054         door_1_done = (a == end);
3055       }
3056
3057       if (door_state & DOOR_ACTION_2)
3058       {
3059         int a = MIN(x * door_2.step_offset, door_size);
3060         int p = (door_state & DOOR_OPEN_2 ? door_size - a : a);
3061         int i = p + door_skip;
3062
3063         if (door_2.anim_mode & ANIM_STATIC_PANEL)
3064         {
3065           BlitBitmap(bitmap_db_door, drawto,
3066                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2,
3067                      VXSIZE, VYSIZE, VX, VY);
3068         }
3069         else if (x <= VYSIZE)
3070         {
3071           BlitBitmap(bitmap_db_door, drawto,
3072                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + p / 2,
3073                      VXSIZE, VYSIZE - p / 2, VX, VY);
3074
3075           ClearRectangle(drawto, VX, VY + VYSIZE - p / 2, VXSIZE, p / 2);
3076         }
3077
3078         if (door_2.anim_mode & ANIM_HORIZONTAL && x <= VXSIZE)
3079         {
3080           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
3081           int dst1_x = VX + VXSIZE - i, dst1_y = VY;
3082           int src2_x = VXSIZE - i,      src2_y = DOOR_GFX_PAGEY2;
3083           int dst2_x = VX,              dst2_y = VY;
3084           int width = i, height = VYSIZE;
3085
3086           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
3087           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
3088                            dst1_x, dst1_y);
3089
3090           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
3091           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
3092                            dst2_x, dst2_y);
3093         }
3094         else if (door_2.anim_mode & ANIM_VERTICAL && x <= VYSIZE)
3095         {
3096           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
3097           int dst1_x = VX,              dst1_y = VY + VYSIZE - i;
3098           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY2 + VYSIZE - i;
3099           int dst2_x = VX,              dst2_y = VY;
3100           int width = VXSIZE, height = i;
3101
3102           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
3103           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
3104                            dst1_x, dst1_y);
3105
3106           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
3107           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
3108                            dst2_x, dst2_y);
3109         }
3110         else if (x <= VXSIZE)   /* ANIM_DEFAULT */
3111         {
3112           int j = (door_2.anim_mode == ANIM_DEFAULT ? (VXSIZE - i) / 3 : 0);
3113
3114           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
3115           BlitBitmapMasked(bitmap, drawto,
3116                            VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
3117                            VX + VXSIZE - i, VY + j);
3118           SetClipOrigin(bitmap, gc,
3119                         VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
3120           BlitBitmapMasked(bitmap, drawto,
3121                            VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
3122                            VX, VY);
3123
3124           BlitBitmapMasked(bitmap, drawto,
3125                            VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
3126                            i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
3127           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
3128           BlitBitmapMasked(bitmap, drawto,
3129                            VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
3130                            i, VYSIZE / 2 - j,
3131                            VX + VXSIZE - i, VY + VYSIZE / 2 + j);
3132         }
3133
3134         redraw_mask |= REDRAW_DOOR_2;
3135         door_2_done = (a == VXSIZE);
3136       }
3137
3138       if (!(door_state & DOOR_NO_DELAY))
3139       {
3140         BackToFront();
3141
3142         if (game_status == GAME_MODE_MAIN)
3143           DoAnimation();
3144
3145         WaitUntilDelayReached(&door_delay, door_delay_value);
3146       }
3147     }
3148   }
3149
3150   if (door_state & DOOR_ACTION_1)
3151     door1 = door_state & DOOR_ACTION_1;
3152   if (door_state & DOOR_ACTION_2)
3153     door2 = door_state & DOOR_ACTION_2;
3154
3155   return (door1 | door2);
3156 }
3157
3158 void DrawSpecialEditorDoor()
3159 {
3160   /* draw bigger toolbox window */
3161   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3162              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
3163              EX - 4, EY - 12);
3164   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
3165              EX - 6, VY - 4, EXSIZE + 12, EYSIZE - VYSIZE + 4,
3166              EX - 6, EY - 4);
3167
3168   redraw_mask |= REDRAW_ALL;
3169 }
3170
3171 void UndrawSpecialEditorDoor()
3172 {
3173   /* draw normal tape recorder window */
3174   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
3175              EX - 6, EY - 12, EXSIZE + 12, EYSIZE - VYSIZE + 12,
3176              EX - 6, EY - 12);
3177
3178   redraw_mask |= REDRAW_ALL;
3179 }
3180
3181
3182 /* ---------- new tool button stuff ---------------------------------------- */
3183
3184 /* graphic position values for tool buttons */
3185 #define TOOL_BUTTON_YES_XPOS            2
3186 #define TOOL_BUTTON_YES_YPOS            250
3187 #define TOOL_BUTTON_YES_GFX_YPOS        0
3188 #define TOOL_BUTTON_YES_XSIZE           46
3189 #define TOOL_BUTTON_YES_YSIZE           28
3190 #define TOOL_BUTTON_NO_XPOS             52
3191 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
3192 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
3193 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
3194 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
3195 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
3196 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
3197 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
3198 #define TOOL_BUTTON_CONFIRM_XSIZE       96
3199 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
3200 #define TOOL_BUTTON_PLAYER_XSIZE        30
3201 #define TOOL_BUTTON_PLAYER_YSIZE        30
3202 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
3203 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
3204 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
3205 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
3206 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
3207                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
3208 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
3209                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
3210 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
3211                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
3212 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
3213                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
3214 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
3215                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
3216 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
3217                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
3218 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
3219                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
3220 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
3221                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
3222
3223 static struct
3224 {
3225   int xpos, ypos;
3226   int x, y;
3227   int width, height;
3228   int gadget_id;
3229   char *infotext;
3230 } toolbutton_info[NUM_TOOL_BUTTONS] =
3231 {
3232   {
3233     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
3234     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
3235     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
3236     TOOL_CTRL_ID_YES,
3237     "yes"
3238   },
3239   {
3240     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
3241     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
3242     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
3243     TOOL_CTRL_ID_NO,
3244     "no"
3245   },
3246   {
3247     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
3248     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
3249     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
3250     TOOL_CTRL_ID_CONFIRM,
3251     "confirm"
3252   },
3253   {
3254     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
3255     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
3256     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
3257     TOOL_CTRL_ID_PLAYER_1,
3258     "player 1"
3259   },
3260   {
3261     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
3262     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
3263     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
3264     TOOL_CTRL_ID_PLAYER_2,
3265     "player 2"
3266   },
3267   {
3268     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
3269     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
3270     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
3271     TOOL_CTRL_ID_PLAYER_3,
3272     "player 3"
3273   },
3274   {
3275     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
3276     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
3277     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
3278     TOOL_CTRL_ID_PLAYER_4,
3279     "player 4"
3280   }
3281 };
3282
3283 void CreateToolButtons()
3284 {
3285   int i;
3286
3287   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3288   {
3289     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
3290     Bitmap *deco_bitmap = None;
3291     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
3292     struct GadgetInfo *gi;
3293     unsigned long event_mask;
3294     int gd_xoffset, gd_yoffset;
3295     int gd_x1, gd_x2, gd_y;
3296     int id = i;
3297
3298     event_mask = GD_EVENT_RELEASED;
3299
3300     gd_xoffset = toolbutton_info[i].xpos;
3301     gd_yoffset = toolbutton_info[i].ypos;
3302     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
3303     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
3304     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
3305
3306     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
3307     {
3308       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
3309
3310       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
3311                            &deco_bitmap, &deco_x, &deco_y);
3312       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
3313       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
3314     }
3315
3316     gi = CreateGadget(GDI_CUSTOM_ID, id,
3317                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
3318                       GDI_X, DX + toolbutton_info[i].x,
3319                       GDI_Y, DY + toolbutton_info[i].y,
3320                       GDI_WIDTH, toolbutton_info[i].width,
3321                       GDI_HEIGHT, toolbutton_info[i].height,
3322                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
3323                       GDI_STATE, GD_BUTTON_UNPRESSED,
3324                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
3325                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
3326                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
3327                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
3328                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
3329                       GDI_DECORATION_SHIFTING, 1, 1,
3330                       GDI_DIRECT_DRAW, FALSE,
3331                       GDI_EVENT_MASK, event_mask,
3332                       GDI_CALLBACK_ACTION, HandleToolButtons,
3333                       GDI_END);
3334
3335     if (gi == NULL)
3336       Error(ERR_EXIT, "cannot create gadget");
3337
3338     tool_gadget[id] = gi;
3339   }
3340 }
3341
3342 void FreeToolButtons()
3343 {
3344   int i;
3345
3346   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3347     FreeGadget(tool_gadget[i]);
3348 }
3349
3350 static void UnmapToolButtons()
3351 {
3352   int i;
3353
3354   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3355     UnmapGadget(tool_gadget[i]);
3356 }
3357
3358 static void HandleToolButtons(struct GadgetInfo *gi)
3359 {
3360   request_gadget_id = gi->custom_id;
3361 }
3362
3363 static struct Mapping_EM_to_RND_object
3364 {
3365   int element_em;
3366   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
3367   boolean is_backside;                  /* backside of moving element */
3368
3369   int element_rnd;
3370   int action;
3371   int direction;
3372 }
3373 em_object_mapping_list[] =
3374 {
3375   {
3376     Xblank,                             TRUE,   FALSE,
3377     EL_EMPTY,                           -1, -1
3378   },
3379   {
3380     Yacid_splash_eB,                    FALSE,  FALSE,
3381     EL_ACID_SPLASH_RIGHT,               -1, -1
3382   },
3383   {
3384     Yacid_splash_wB,                    FALSE,  FALSE,
3385     EL_ACID_SPLASH_LEFT,                -1, -1
3386   },
3387
3388 #ifdef EM_ENGINE_BAD_ROLL
3389   {
3390     Xstone_force_e,                     FALSE,  FALSE,
3391     EL_ROCK,                            -1, MV_BIT_RIGHT
3392   },
3393   {
3394     Xstone_force_w,                     FALSE,  FALSE,
3395     EL_ROCK,                            -1, MV_BIT_LEFT
3396   },
3397   {
3398     Xnut_force_e,                       FALSE,  FALSE,
3399     EL_NUT,                             -1, MV_BIT_RIGHT
3400   },
3401   {
3402     Xnut_force_w,                       FALSE,  FALSE,
3403     EL_NUT,                             -1, MV_BIT_LEFT
3404   },
3405   {
3406     Xspring_force_e,                    FALSE,  FALSE,
3407     EL_SPRING,                          -1, MV_BIT_RIGHT
3408   },
3409   {
3410     Xspring_force_w,                    FALSE,  FALSE,
3411     EL_SPRING,                          -1, MV_BIT_LEFT
3412   },
3413   {
3414     Xemerald_force_e,                   FALSE,  FALSE,
3415     EL_EMERALD,                         -1, MV_BIT_RIGHT
3416   },
3417   {
3418     Xemerald_force_w,                   FALSE,  FALSE,
3419     EL_EMERALD,                         -1, MV_BIT_LEFT
3420   },
3421   {
3422     Xdiamond_force_e,                   FALSE,  FALSE,
3423     EL_DIAMOND,                         -1, MV_BIT_RIGHT
3424   },
3425   {
3426     Xdiamond_force_w,                   FALSE,  FALSE,
3427     EL_DIAMOND,                         -1, MV_BIT_LEFT
3428   },
3429   {
3430     Xbomb_force_e,                      FALSE,  FALSE,
3431     EL_BOMB,                            -1, MV_BIT_RIGHT
3432   },
3433   {
3434     Xbomb_force_w,                      FALSE,  FALSE,
3435     EL_BOMB,                            -1, MV_BIT_LEFT
3436   },
3437 #endif  /* EM_ENGINE_BAD_ROLL */
3438
3439   {
3440     Xstone,                             TRUE,   FALSE,
3441     EL_ROCK,                            -1, -1
3442   },
3443   {
3444     Xstone_pause,                       FALSE,  FALSE,
3445     EL_ROCK,                            -1, -1
3446   },
3447   {
3448     Xstone_fall,                        FALSE,  FALSE,
3449     EL_ROCK,                            -1, -1
3450   },
3451   {
3452     Ystone_s,                           FALSE,  FALSE,
3453     EL_ROCK,                            ACTION_FALLING, -1
3454   },
3455   {
3456     Ystone_sB,                          FALSE,  TRUE,
3457     EL_ROCK,                            ACTION_FALLING, -1
3458   },
3459   {
3460     Ystone_e,                           FALSE,  FALSE,
3461     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
3462   },
3463   {
3464     Ystone_eB,                          FALSE,  TRUE,
3465     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
3466   },
3467   {
3468     Ystone_w,                           FALSE,  FALSE,
3469     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
3470   },
3471   {
3472     Ystone_wB,                          FALSE,  TRUE,
3473     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
3474   },
3475   {
3476     Xnut,                               TRUE,   FALSE,
3477     EL_NUT,                             -1, -1
3478   },
3479   {
3480     Xnut_pause,                         FALSE,  FALSE,
3481     EL_NUT,                             -1, -1
3482   },
3483   {
3484     Xnut_fall,                          FALSE,  FALSE,
3485     EL_NUT,                             -1, -1
3486   },
3487   {
3488     Ynut_s,                             FALSE,  FALSE,
3489     EL_NUT,                             ACTION_FALLING, -1
3490   },
3491   {
3492     Ynut_sB,                            FALSE,  TRUE,
3493     EL_NUT,                             ACTION_FALLING, -1
3494   },
3495   {
3496     Ynut_e,                             FALSE,  FALSE,
3497     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
3498   },
3499   {
3500     Ynut_eB,                            FALSE,  TRUE,
3501     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
3502   },
3503   {
3504     Ynut_w,                             FALSE,  FALSE,
3505     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
3506   },
3507   {
3508     Ynut_wB,                            FALSE,  TRUE,
3509     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
3510   },
3511   {
3512     Xbug_n,                             TRUE,   FALSE,
3513     EL_BUG_UP,                          -1, -1
3514   },
3515   {
3516     Xbug_e,                             TRUE,   FALSE,
3517     EL_BUG_RIGHT,                       -1, -1
3518   },
3519   {
3520     Xbug_s,                             TRUE,   FALSE,
3521     EL_BUG_DOWN,                        -1, -1
3522   },
3523   {
3524     Xbug_w,                             TRUE,   FALSE,
3525     EL_BUG_LEFT,                        -1, -1
3526   },
3527   {
3528     Xbug_gon,                           FALSE,  FALSE,
3529     EL_BUG_UP,                          -1, -1
3530   },
3531   {
3532     Xbug_goe,                           FALSE,  FALSE,
3533     EL_BUG_RIGHT,                       -1, -1
3534   },
3535   {
3536     Xbug_gos,                           FALSE,  FALSE,
3537     EL_BUG_DOWN,                        -1, -1
3538   },
3539   {
3540     Xbug_gow,                           FALSE,  FALSE,
3541     EL_BUG_LEFT,                        -1, -1
3542   },
3543   {
3544     Ybug_n,                             FALSE,  FALSE,
3545     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
3546   },
3547   {
3548     Ybug_nB,                            FALSE,  TRUE,
3549     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
3550   },
3551   {
3552     Ybug_e,                             FALSE,  FALSE,
3553     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
3554   },
3555   {
3556     Ybug_eB,                            FALSE,  TRUE,
3557     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
3558   },
3559   {
3560     Ybug_s,                             FALSE,  FALSE,
3561     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
3562   },
3563   {
3564     Ybug_sB,                            FALSE,  TRUE,
3565     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
3566   },
3567   {
3568     Ybug_w,                             FALSE,  FALSE,
3569     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
3570   },
3571   {
3572     Ybug_wB,                            FALSE,  TRUE,
3573     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
3574   },
3575   {
3576     Ybug_w_n,                           FALSE,  FALSE,
3577     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
3578   },
3579   {
3580     Ybug_n_e,                           FALSE,  FALSE,
3581     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
3582   },
3583   {
3584     Ybug_e_s,                           FALSE,  FALSE,
3585     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
3586   },
3587   {
3588     Ybug_s_w,                           FALSE,  FALSE,
3589     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
3590   },
3591   {
3592     Ybug_e_n,                           FALSE,  FALSE,
3593     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
3594   },
3595   {
3596     Ybug_s_e,                           FALSE,  FALSE,
3597     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
3598   },
3599   {
3600     Ybug_w_s,                           FALSE,  FALSE,
3601     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
3602   },
3603   {
3604     Ybug_n_w,                           FALSE,  FALSE,
3605     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
3606   },
3607   {
3608     Ybug_stone,                         FALSE,  FALSE,
3609     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
3610   },
3611   {
3612     Ybug_spring,                        FALSE,  FALSE,
3613     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
3614   },
3615   {
3616     Xtank_n,                            TRUE,   FALSE,
3617     EL_SPACESHIP_UP,                    -1, -1
3618   },
3619   {
3620     Xtank_e,                            TRUE,   FALSE,
3621     EL_SPACESHIP_RIGHT,                 -1, -1
3622   },
3623   {
3624     Xtank_s,                            TRUE,   FALSE,
3625     EL_SPACESHIP_DOWN,                  -1, -1
3626   },
3627   {
3628     Xtank_w,                            TRUE,   FALSE,
3629     EL_SPACESHIP_LEFT,                  -1, -1
3630   },
3631   {
3632     Xtank_gon,                          FALSE,  FALSE,
3633     EL_SPACESHIP_UP,                    -1, -1
3634   },
3635   {
3636     Xtank_goe,                          FALSE,  FALSE,
3637     EL_SPACESHIP_RIGHT,                 -1, -1
3638   },
3639   {
3640     Xtank_gos,                          FALSE,  FALSE,
3641     EL_SPACESHIP_DOWN,                  -1, -1
3642   },
3643   {
3644     Xtank_gow,                          FALSE,  FALSE,
3645     EL_SPACESHIP_LEFT,                  -1, -1
3646   },
3647   {
3648     Ytank_n,                            FALSE,  FALSE,
3649     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
3650   },
3651   {
3652     Ytank_nB,                           FALSE,  TRUE,
3653     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
3654   },
3655   {
3656     Ytank_e,                            FALSE,  FALSE,
3657     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
3658   },
3659   {
3660     Ytank_eB,                           FALSE,  TRUE,
3661     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
3662   },
3663   {
3664     Ytank_s,                            FALSE,  FALSE,
3665     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
3666   },
3667   {
3668     Ytank_sB,                           FALSE,  TRUE,
3669     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
3670   },
3671   {
3672     Ytank_w,                            FALSE,  FALSE,
3673     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
3674   },
3675   {
3676     Ytank_wB,                           FALSE,  TRUE,
3677     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
3678   },
3679   {
3680     Ytank_w_n,                          FALSE,  FALSE,
3681     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
3682   },
3683   {
3684     Ytank_n_e,                          FALSE,  FALSE,
3685     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
3686   },
3687   {
3688     Ytank_e_s,                          FALSE,  FALSE,
3689     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
3690   },
3691   {
3692     Ytank_s_w,                          FALSE,  FALSE,
3693     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
3694   },
3695   {
3696     Ytank_e_n,                          FALSE,  FALSE,
3697     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
3698   },
3699   {
3700     Ytank_s_e,                          FALSE,  FALSE,
3701     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
3702   },
3703   {
3704     Ytank_w_s,                          FALSE,  FALSE,
3705     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
3706   },
3707   {
3708     Ytank_n_w,                          FALSE,  FALSE,
3709     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
3710   },
3711   {
3712     Ytank_stone,                        FALSE,  FALSE,
3713     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
3714   },
3715   {
3716     Ytank_spring,                       FALSE,  FALSE,
3717     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
3718   },
3719   {
3720     Xandroid,                           TRUE,   FALSE,
3721     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
3722   },
3723   {
3724     Xandroid_1_n,                       FALSE,  FALSE,
3725     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
3726   },
3727   {
3728     Xandroid_2_n,                       FALSE,  FALSE,
3729     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
3730   },
3731   {
3732     Xandroid_1_e,                       FALSE,  FALSE,
3733     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
3734   },
3735   {
3736     Xandroid_2_e,                       FALSE,  FALSE,
3737     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
3738   },
3739   {
3740     Xandroid_1_w,                       FALSE,  FALSE,
3741     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
3742   },
3743   {
3744     Xandroid_2_w,                       FALSE,  FALSE,
3745     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
3746   },
3747   {
3748     Xandroid_1_s,                       FALSE,  FALSE,
3749     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
3750   },
3751   {
3752     Xandroid_2_s,                       FALSE,  FALSE,
3753     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
3754   },
3755   {
3756     Yandroid_n,                         FALSE,  FALSE,
3757     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
3758   },
3759   {
3760     Yandroid_nB,                        FALSE,  TRUE,
3761     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
3762   },
3763   {
3764     Yandroid_ne,                        FALSE,  FALSE,
3765     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
3766   },
3767   {
3768     Yandroid_neB,                       FALSE,  TRUE,
3769     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
3770   },
3771   {
3772     Yandroid_e,                         FALSE,  FALSE,
3773     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
3774   },
3775   {
3776     Yandroid_eB,                        FALSE,  TRUE,
3777     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
3778   },
3779   {
3780     Yandroid_se,                        FALSE,  FALSE,
3781     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
3782   },
3783   {
3784     Yandroid_seB,                       FALSE,  TRUE,
3785     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
3786   },
3787   {
3788     Yandroid_s,                         FALSE,  FALSE,
3789     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
3790   },
3791   {
3792     Yandroid_sB,                        FALSE,  TRUE,
3793     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
3794   },
3795   {
3796     Yandroid_sw,                        FALSE,  FALSE,
3797     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
3798   },
3799   {
3800     Yandroid_swB,                       FALSE,  TRUE,
3801     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
3802   },
3803   {
3804     Yandroid_w,                         FALSE,  FALSE,
3805     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
3806   },
3807   {
3808     Yandroid_wB,                        FALSE,  TRUE,
3809     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
3810   },
3811   {
3812     Yandroid_nw,                        FALSE,  FALSE,
3813     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
3814   },
3815   {
3816     Yandroid_nwB,                       FALSE,  TRUE,
3817     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
3818   },
3819   {
3820     Xspring,                            TRUE,   FALSE,
3821     EL_SPRING,                          -1, -1
3822   },
3823   {
3824     Xspring_pause,                      FALSE,  FALSE,
3825     EL_SPRING,                          -1, -1
3826   },
3827   {
3828     Xspring_e,                          FALSE,  FALSE,
3829     EL_SPRING,                          -1, -1
3830   },
3831   {
3832     Xspring_w,                          FALSE,  FALSE,
3833     EL_SPRING,                          -1, -1
3834   },
3835   {
3836     Xspring_fall,                       FALSE,  FALSE,
3837     EL_SPRING,                          -1, -1
3838   },
3839   {
3840     Yspring_s,                          FALSE,  FALSE,
3841     EL_SPRING,                          ACTION_FALLING, -1
3842   },
3843   {
3844     Yspring_sB,                         FALSE,  TRUE,
3845     EL_SPRING,                          ACTION_FALLING, -1
3846   },
3847   {
3848     Yspring_e,                          FALSE,  FALSE,
3849     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
3850   },
3851   {
3852     Yspring_eB,                         FALSE,  TRUE,
3853     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
3854   },
3855   {
3856     Yspring_w,                          FALSE,  FALSE,
3857     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
3858   },
3859   {
3860     Yspring_wB,                         FALSE,  TRUE,
3861     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
3862   },
3863   {
3864     Yspring_kill_e,                     FALSE,  FALSE,
3865     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
3866   },
3867   {
3868     Yspring_kill_eB,                    FALSE,  TRUE,
3869     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
3870   },
3871   {
3872     Yspring_kill_w,                     FALSE,  FALSE,
3873     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
3874   },
3875   {
3876     Yspring_kill_wB,                    FALSE,  TRUE,
3877     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
3878   },
3879   {
3880     Xeater_n,                           TRUE,   FALSE,
3881     EL_YAMYAM_UP,                       -1, -1
3882   },
3883   {
3884     Xeater_e,                           TRUE,   FALSE,
3885     EL_YAMYAM_RIGHT,                    -1, -1
3886   },
3887   {
3888     Xeater_w,                           TRUE,   FALSE,
3889     EL_YAMYAM_LEFT,                     -1, -1
3890   },
3891   {
3892     Xeater_s,                           TRUE,   FALSE,
3893     EL_YAMYAM_DOWN,                     -1, -1
3894   },
3895   {
3896     Yeater_n,                           FALSE,  FALSE,
3897     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
3898   },
3899   {
3900     Yeater_nB,                          FALSE,  TRUE,
3901     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
3902   },
3903   {
3904     Yeater_e,                           FALSE,  FALSE,
3905     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
3906   },
3907   {
3908     Yeater_eB,                          FALSE,  TRUE,
3909     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
3910   },
3911   {
3912     Yeater_s,                           FALSE,  FALSE,
3913     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
3914   },
3915   {
3916     Yeater_sB,                          FALSE,  TRUE,
3917     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
3918   },
3919   {
3920     Yeater_w,                           FALSE,  FALSE,
3921     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
3922   },
3923   {
3924     Yeater_wB,                          FALSE,  TRUE,
3925     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
3926   },
3927   {
3928     Yeater_stone,                       FALSE,  FALSE,
3929     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
3930   },
3931   {
3932     Yeater_spring,                      FALSE,  FALSE,
3933     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
3934   },
3935   {
3936     Xalien,                             TRUE,   FALSE,
3937     EL_ROBOT,                           -1, -1
3938   },
3939   {
3940     Xalien_pause,                       FALSE,  FALSE,
3941     EL_ROBOT,                           -1, -1
3942   },
3943   {
3944     Yalien_n,                           FALSE,  FALSE,
3945     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
3946   },
3947   {
3948     Yalien_nB,                          FALSE,  TRUE,
3949     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
3950   },
3951   {
3952     Yalien_e,                           FALSE,  FALSE,
3953     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
3954   },
3955   {
3956     Yalien_eB,                          FALSE,  TRUE,
3957     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
3958   },
3959   {
3960     Yalien_s,                           FALSE,  FALSE,
3961     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
3962   },
3963   {
3964     Yalien_sB,                          FALSE,  TRUE,
3965     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
3966   },
3967   {
3968     Yalien_w,                           FALSE,  FALSE,
3969     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
3970   },
3971   {
3972     Yalien_wB,                          FALSE,  TRUE,
3973     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
3974   },
3975   {
3976     Yalien_stone,                       FALSE,  FALSE,
3977     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
3978   },
3979   {
3980     Yalien_spring,                      FALSE,  FALSE,
3981     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
3982   },
3983   {
3984     Xemerald,                           TRUE,   FALSE,
3985     EL_EMERALD,                         -1, -1
3986   },
3987   {
3988     Xemerald_pause,                     FALSE,  FALSE,
3989     EL_EMERALD,                         -1, -1
3990   },
3991   {
3992     Xemerald_fall,                      FALSE,  FALSE,
3993     EL_EMERALD,                         -1, -1
3994   },
3995   {
3996     Xemerald_shine,                     FALSE,  FALSE,
3997     EL_EMERALD,                         ACTION_TWINKLING, -1
3998   },
3999   {
4000     Yemerald_s,                         FALSE,  FALSE,
4001     EL_EMERALD,                         ACTION_FALLING, -1
4002   },
4003   {
4004     Yemerald_sB,                        FALSE,  TRUE,
4005     EL_EMERALD,                         ACTION_FALLING, -1
4006   },
4007   {
4008     Yemerald_e,                         FALSE,  FALSE,
4009     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
4010   },
4011   {
4012     Yemerald_eB,                        FALSE,  TRUE,
4013     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
4014   },
4015   {
4016     Yemerald_w,                         FALSE,  FALSE,
4017     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
4018   },
4019   {
4020     Yemerald_wB,                        FALSE,  TRUE,
4021     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
4022   },
4023   {
4024     Yemerald_eat,                       FALSE,  FALSE,
4025     EL_EMERALD,                         ACTION_COLLECTING, -1
4026   },
4027   {
4028     Yemerald_stone,                     FALSE,  FALSE,
4029     EL_NUT,                             ACTION_BREAKING, -1
4030   },
4031   {
4032     Xdiamond,                           TRUE,   FALSE,
4033     EL_DIAMOND,                         -1, -1
4034   },
4035   {
4036     Xdiamond_pause,                     FALSE,  FALSE,
4037     EL_DIAMOND,                         -1, -1
4038   },
4039   {
4040     Xdiamond_fall,                      FALSE,  FALSE,
4041     EL_DIAMOND,                         -1, -1
4042   },
4043   {
4044     Xdiamond_shine,                     FALSE,  FALSE,
4045     EL_DIAMOND,                         ACTION_TWINKLING, -1
4046   },
4047   {
4048     Ydiamond_s,                         FALSE,  FALSE,
4049     EL_DIAMOND,                         ACTION_FALLING, -1
4050   },
4051   {
4052     Ydiamond_sB,                        FALSE,  TRUE,
4053     EL_DIAMOND,                         ACTION_FALLING, -1
4054   },
4055   {
4056     Ydiamond_e,                         FALSE,  FALSE,
4057     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
4058   },
4059   {
4060     Ydiamond_eB,                        FALSE,  TRUE,
4061     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
4062   },
4063   {
4064     Ydiamond_w,                         FALSE,  FALSE,
4065     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
4066   },
4067   {
4068     Ydiamond_wB,                        FALSE,  TRUE,
4069     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
4070   },
4071   {
4072     Ydiamond_eat,                       FALSE,  FALSE,
4073     EL_DIAMOND,                         ACTION_COLLECTING, -1
4074   },
4075   {
4076     Ydiamond_stone,                     FALSE,  FALSE,
4077     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
4078   },
4079   {
4080     Xdrip_fall,                         TRUE,   FALSE,
4081     EL_AMOEBA_DROP,                     -1, -1
4082   },
4083   {
4084     Xdrip_stretch,                      FALSE,  FALSE,
4085     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4086   },
4087   {
4088     Xdrip_stretchB,                     FALSE,  TRUE,
4089     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4090   },
4091   {
4092     Xdrip_eat,                          FALSE,  FALSE,
4093     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
4094   },
4095   {
4096     Ydrip_s1,                           FALSE,  FALSE,
4097     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4098   },
4099   {
4100     Ydrip_s1B,                          FALSE,  TRUE,
4101     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4102   },
4103   {
4104     Ydrip_s2,                           FALSE,  FALSE,
4105     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4106   },
4107   {
4108     Ydrip_s2B,                          FALSE,  TRUE,
4109     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
4110   },
4111   {
4112     Xbomb,                              TRUE,   FALSE,
4113     EL_BOMB,                            -1, -1
4114   },
4115   {
4116     Xbomb_pause,                        FALSE,  FALSE,
4117     EL_BOMB,                            -1, -1
4118   },
4119   {
4120     Xbomb_fall,                         FALSE,  FALSE,
4121     EL_BOMB,                            -1, -1
4122   },
4123   {
4124     Ybomb_s,                            FALSE,  FALSE,
4125     EL_BOMB,                            ACTION_FALLING, -1
4126   },
4127   {
4128     Ybomb_sB,                           FALSE,  TRUE,
4129     EL_BOMB,                            ACTION_FALLING, -1
4130   },
4131   {
4132     Ybomb_e,                            FALSE,  FALSE,
4133     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
4134   },
4135   {
4136     Ybomb_eB,                           FALSE,  TRUE,
4137     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
4138   },
4139   {
4140     Ybomb_w,                            FALSE,  FALSE,
4141     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
4142   },
4143   {
4144     Ybomb_wB,                           FALSE,  TRUE,
4145     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
4146   },
4147   {
4148     Ybomb_eat,                          FALSE,  FALSE,
4149     EL_BOMB,                            ACTION_ACTIVATING, -1
4150   },
4151   {
4152     Xballoon,                           TRUE,   FALSE,
4153     EL_BALLOON,                         -1, -1
4154   },
4155   {
4156     Yballoon_n,                         FALSE,  FALSE,
4157     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
4158   },
4159   {
4160     Yballoon_nB,                        FALSE,  TRUE,
4161     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
4162   },
4163   {
4164     Yballoon_e,                         FALSE,  FALSE,
4165     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
4166   },
4167   {
4168     Yballoon_eB,                        FALSE,  TRUE,
4169     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
4170   },
4171   {
4172     Yballoon_s,                         FALSE,  FALSE,
4173     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
4174   },
4175   {
4176     Yballoon_sB,                        FALSE,  TRUE,
4177     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
4178   },
4179   {
4180     Yballoon_w,                         FALSE,  FALSE,
4181     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
4182   },
4183   {
4184     Yballoon_wB,                        FALSE,  TRUE,
4185     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
4186   },
4187   {
4188     Xgrass,                             TRUE,   FALSE,
4189     EL_EMC_GRASS,                       -1, -1
4190   },
4191   {
4192     Ygrass_nB,                          FALSE,  FALSE,
4193     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
4194   },
4195   {
4196     Ygrass_eB,                          FALSE,  FALSE,
4197     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
4198   },
4199   {
4200     Ygrass_sB,                          FALSE,  FALSE,
4201     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
4202   },
4203   {
4204     Ygrass_wB,                          FALSE,  FALSE,
4205     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
4206   },
4207   {
4208     Xdirt,                              TRUE,   FALSE,
4209     EL_SAND,                            -1, -1
4210   },
4211   {
4212     Ydirt_nB,                           FALSE,  FALSE,
4213     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
4214   },
4215   {
4216     Ydirt_eB,                           FALSE,  FALSE,
4217     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
4218   },
4219   {
4220     Ydirt_sB,                           FALSE,  FALSE,
4221     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
4222   },
4223   {
4224     Ydirt_wB,                           FALSE,  FALSE,
4225     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
4226   },
4227   {
4228     Xacid_ne,                           TRUE,   FALSE,
4229     EL_ACID_POOL_TOPRIGHT,              -1, -1
4230   },
4231   {
4232     Xacid_se,                           TRUE,   FALSE,
4233     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
4234   },
4235   {
4236     Xacid_s,                            TRUE,   FALSE,
4237     EL_ACID_POOL_BOTTOM,                -1, -1
4238   },
4239   {
4240     Xacid_sw,                           TRUE,   FALSE,
4241     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
4242   },
4243   {
4244     Xacid_nw,                           TRUE,   FALSE,
4245     EL_ACID_POOL_TOPLEFT,               -1, -1
4246   },
4247   {
4248     Xacid_1,                            TRUE,   FALSE,
4249     EL_ACID,                            -1, -1
4250   },
4251   {
4252     Xacid_2,                            FALSE,  FALSE,
4253     EL_ACID,                            -1, -1
4254   },
4255   {
4256     Xacid_3,                            FALSE,  FALSE,
4257     EL_ACID,                            -1, -1
4258   },
4259   {
4260     Xacid_4,                            FALSE,  FALSE,
4261     EL_ACID,                            -1, -1
4262   },
4263   {
4264     Xacid_5,                            FALSE,  FALSE,
4265     EL_ACID,                            -1, -1
4266   },
4267   {
4268     Xacid_6,                            FALSE,  FALSE,
4269     EL_ACID,                            -1, -1
4270   },
4271   {
4272     Xacid_7,                            FALSE,  FALSE,
4273     EL_ACID,                            -1, -1
4274   },
4275   {
4276     Xacid_8,                            FALSE,  FALSE,
4277     EL_ACID,                            -1, -1
4278   },
4279   {
4280     Xball_1,                            TRUE,   FALSE,
4281     EL_EMC_MAGIC_BALL,                  -1, -1
4282   },
4283   {
4284     Xball_1B,                           FALSE,  FALSE,
4285     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
4286   },
4287   {
4288     Xball_2,                            FALSE,  FALSE,
4289     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
4290   },
4291   {
4292     Xball_2B,                           FALSE,  FALSE,
4293     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
4294   },
4295   {
4296     Yball_eat,                          FALSE,  FALSE,
4297     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
4298   },
4299   {
4300     Ykey_1_eat,                         FALSE,  FALSE,
4301     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
4302   },
4303   {
4304     Ykey_2_eat,                         FALSE,  FALSE,
4305     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
4306   },
4307   {
4308     Ykey_3_eat,                         FALSE,  FALSE,
4309     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
4310   },
4311   {
4312     Ykey_4_eat,                         FALSE,  FALSE,
4313     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
4314   },
4315   {
4316     Ykey_5_eat,                         FALSE,  FALSE,
4317     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
4318   },
4319   {
4320     Ykey_6_eat,                         FALSE,  FALSE,
4321     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
4322   },
4323   {
4324     Ykey_7_eat,                         FALSE,  FALSE,
4325     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
4326   },
4327   {
4328     Ykey_8_eat,                         FALSE,  FALSE,
4329     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
4330   },
4331   {
4332     Ylenses_eat,                        FALSE,  FALSE,
4333     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
4334   },
4335   {
4336     Ymagnify_eat,                       FALSE,  FALSE,
4337     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
4338   },
4339   {
4340     Ygrass_eat,                         FALSE,  FALSE,
4341     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
4342   },
4343   {
4344     Ydirt_eat,                          FALSE,  FALSE,
4345     EL_SAND,                            ACTION_SNAPPING, -1
4346   },
4347   {
4348     Xgrow_ns,                           TRUE,   FALSE,
4349     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
4350   },
4351   {
4352     Ygrow_ns_eat,                       FALSE,  FALSE,
4353     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
4354   },
4355   {
4356     Xgrow_ew,                           TRUE,   FALSE,
4357     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
4358   },
4359   {
4360     Ygrow_ew_eat,                       FALSE,  FALSE,
4361     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
4362   },
4363   {
4364     Xwonderwall,                        TRUE,   FALSE,
4365     EL_MAGIC_WALL,                      -1, -1
4366   },
4367   {
4368     XwonderwallB,                       FALSE,  FALSE,
4369     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
4370   },
4371   {
4372     Xamoeba_1,                          TRUE,   FALSE,
4373     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
4374   },
4375   {
4376     Xamoeba_2,                          FALSE,  FALSE,
4377     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
4378   },
4379   {
4380     Xamoeba_3,                          FALSE,  FALSE,
4381     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
4382   },
4383   {
4384     Xamoeba_4,                          FALSE,  FALSE,
4385     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
4386   },
4387   {
4388     Xamoeba_5,                          TRUE,   FALSE,
4389     EL_AMOEBA_WET,                      ACTION_OTHER, -1
4390   },
4391   {
4392     Xamoeba_6,                          FALSE,  FALSE,
4393     EL_AMOEBA_WET,                      ACTION_OTHER, -1
4394   },
4395   {
4396     Xamoeba_7,                          FALSE,  FALSE,
4397     EL_AMOEBA_WET,                      ACTION_OTHER, -1
4398   },
4399   {
4400     Xamoeba_8,                          FALSE,  FALSE,
4401     EL_AMOEBA_WET,                      ACTION_OTHER, -1
4402   },
4403   {
4404     Xdoor_1,                            TRUE,   FALSE,
4405     EL_EM_GATE_1,                       -1, -1
4406   },
4407   {
4408     Xdoor_2,                            TRUE,   FALSE,
4409     EL_EM_GATE_2,                       -1, -1
4410   },
4411   {
4412     Xdoor_3,                            TRUE,   FALSE,
4413     EL_EM_GATE_3,                       -1, -1
4414   },
4415   {
4416     Xdoor_4,                            TRUE,   FALSE,
4417     EL_EM_GATE_4,                       -1, -1
4418   },
4419   {
4420     Xdoor_5,                            TRUE,   FALSE,
4421     EL_EMC_GATE_5,                      -1, -1
4422   },
4423   {
4424     Xdoor_6,                            TRUE,   FALSE,
4425     EL_EMC_GATE_6,                      -1, -1
4426   },
4427   {
4428     Xdoor_7,                            TRUE,   FALSE,
4429     EL_EMC_GATE_7,                      -1, -1
4430   },
4431   {
4432     Xdoor_8,                            TRUE,   FALSE,
4433     EL_EMC_GATE_8,                      -1, -1
4434   },
4435   {
4436     Xkey_1,                             TRUE,   FALSE,
4437     EL_EM_KEY_1,                        -1, -1
4438   },
4439   {
4440     Xkey_2,                             TRUE,   FALSE,
4441     EL_EM_KEY_2,                        -1, -1
4442   },
4443   {
4444     Xkey_3,                             TRUE,   FALSE,
4445     EL_EM_KEY_3,                        -1, -1
4446   },
4447   {
4448     Xkey_4,                             TRUE,   FALSE,
4449     EL_EM_KEY_4,                        -1, -1
4450   },
4451   {
4452     Xkey_5,                             TRUE,   FALSE,
4453     EL_EMC_KEY_5,                       -1, -1
4454   },
4455   {
4456     Xkey_6,                             TRUE,   FALSE,
4457     EL_EMC_KEY_6,                       -1, -1
4458   },
4459   {
4460     Xkey_7,                             TRUE,   FALSE,
4461     EL_EMC_KEY_7,                       -1, -1
4462   },
4463   {
4464     Xkey_8,                             TRUE,   FALSE,
4465     EL_EMC_KEY_8,                       -1, -1
4466   },
4467   {
4468     Xwind_n,                            TRUE,   FALSE,
4469     EL_BALLOON_SWITCH_UP,               -1, -1
4470   },
4471   {
4472     Xwind_e,                            TRUE,   FALSE,
4473     EL_BALLOON_SWITCH_RIGHT,            -1, -1
4474   },
4475   {
4476     Xwind_s,                            TRUE,   FALSE,
4477     EL_BALLOON_SWITCH_DOWN,             -1, -1
4478   },
4479   {
4480     Xwind_w,                            TRUE,   FALSE,
4481     EL_BALLOON_SWITCH_LEFT,             -1, -1
4482   },
4483   {
4484     Xwind_nesw,                         TRUE,   FALSE,
4485     EL_BALLOON_SWITCH_ANY,              -1, -1
4486   },
4487   {
4488     Xwind_stop,                         TRUE,   FALSE,
4489     EL_BALLOON_SWITCH_NONE,             -1, -1
4490   },
4491   {
4492     Xexit,                              TRUE,   FALSE,
4493     EL_EM_EXIT_CLOSED,                  -1, -1
4494   },
4495   {
4496     Xexit_1,                            TRUE,   FALSE,
4497     EL_EM_EXIT_OPEN,                    -1, -1
4498   },
4499   {
4500     Xexit_2,                            FALSE,  FALSE,
4501     EL_EM_EXIT_OPEN,                    -1, -1
4502   },
4503   {
4504     Xexit_3,                            FALSE,  FALSE,
4505     EL_EM_EXIT_OPEN,                    -1, -1
4506   },
4507   {
4508     Xdynamite,                          TRUE,   FALSE,
4509     EL_EM_DYNAMITE,                     -1, -1
4510   },
4511   {
4512     Ydynamite_eat,                      FALSE,  FALSE,
4513     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
4514   },
4515   {
4516     Xdynamite_1,                        TRUE,   FALSE,
4517     EL_EM_DYNAMITE_ACTIVE,              -1, -1
4518   },
4519   {
4520     Xdynamite_2,                        FALSE,  FALSE,
4521     EL_EM_DYNAMITE_ACTIVE,              -1, -1
4522   },
4523   {
4524     Xdynamite_3,                        FALSE,  FALSE,
4525     EL_EM_DYNAMITE_ACTIVE,              -1, -1
4526   },
4527   {
4528     Xdynamite_4,                        FALSE,  FALSE,
4529     EL_EM_DYNAMITE_ACTIVE,              -1, -1
4530   },
4531   {
4532     Xbumper,                            TRUE,   FALSE,
4533     EL_EMC_SPRING_BUMPER,               -1, -1
4534   },
4535   {
4536     XbumperB,                           FALSE,  FALSE,
4537     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
4538   },
4539   {
4540     Xwheel,                             TRUE,   FALSE,
4541     EL_ROBOT_WHEEL,                     -1, -1
4542   },
4543   {
4544     XwheelB,                            FALSE,  FALSE,
4545     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
4546   },
4547   {
4548     Xswitch,                            TRUE,   FALSE,
4549     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
4550   },
4551   {
4552     XswitchB,                           FALSE,  FALSE,
4553     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
4554   },
4555   {
4556     Xsand,                              TRUE,   FALSE,
4557     EL_QUICKSAND_EMPTY,                 -1, -1
4558   },
4559   {
4560     Xsand_stone,                        TRUE,   FALSE,
4561     EL_QUICKSAND_FULL,                  -1, -1
4562   },
4563   {
4564     Xsand_stonein_1,                    FALSE,  TRUE,
4565     EL_ROCK,                            ACTION_FILLING, -1
4566   },
4567   {
4568     Xsand_stonein_2,                    FALSE,  TRUE,
4569     EL_ROCK,                            ACTION_FILLING, -1
4570   },
4571   {
4572     Xsand_stonein_3,                    FALSE,  TRUE,
4573     EL_ROCK,                            ACTION_FILLING, -1
4574   },
4575   {
4576     Xsand_stonein_4,                    FALSE,  TRUE,
4577     EL_ROCK,                            ACTION_FILLING, -1
4578   },
4579   {
4580     Xsand_stonesand_1,                  FALSE,  FALSE,
4581     EL_QUICKSAND_FULL,                  -1, -1
4582   },
4583   {
4584     Xsand_stonesand_2,                  FALSE,  FALSE,
4585     EL_QUICKSAND_FULL,                  -1, -1
4586   },
4587   {
4588     Xsand_stonesand_3,                  FALSE,  FALSE,
4589     EL_QUICKSAND_FULL,                  -1, -1
4590   },
4591   {
4592     Xsand_stonesand_4,                  FALSE,  FALSE,
4593     EL_QUICKSAND_FULL,                  -1, -1
4594   },
4595   {
4596     Xsand_stoneout_1,                   FALSE,  FALSE,
4597     EL_ROCK,                            ACTION_EMPTYING, -1
4598   },
4599   {
4600     Xsand_stoneout_2,                   FALSE,  FALSE,
4601     EL_ROCK,                            ACTION_EMPTYING, -1
4602   },
4603   {
4604     Xsand_sandstone_1,                  FALSE,  FALSE,
4605     EL_QUICKSAND_FULL,                  -1, -1
4606   },
4607   {
4608     Xsand_sandstone_2,                  FALSE,  FALSE,
4609     EL_QUICKSAND_FULL,                  -1, -1
4610   },
4611   {
4612     Xsand_sandstone_3,                  FALSE,  FALSE,
4613     EL_QUICKSAND_FULL,                  -1, -1
4614   },
4615   {
4616     Xsand_sandstone_4,                  FALSE,  FALSE,
4617     EL_QUICKSAND_FULL,                  -1, -1
4618   },
4619   {
4620     Xplant,                             TRUE,   FALSE,
4621     EL_EMC_PLANT,                       -1, -1
4622   },
4623   {
4624     Yplant,                             FALSE,  FALSE,
4625     EL_EMC_PLANT,                       -1, -1
4626   },
4627   {
4628     Xlenses,                            TRUE,   FALSE,
4629     EL_EMC_LENSES,                      -1, -1
4630   },
4631   {
4632     Xmagnify,                           TRUE,   FALSE,
4633     EL_EMC_MAGNIFIER,                   -1, -1
4634   },
4635   {
4636     Xdripper,                           TRUE,   FALSE,
4637     EL_EMC_DRIPPER,                     -1, -1
4638   },
4639   {
4640     XdripperB,                          FALSE,  FALSE,
4641     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
4642   },
4643   {
4644     Xfake_blank,                        TRUE,   FALSE,
4645     EL_INVISIBLE_WALL,                  -1, -1
4646   },
4647   {
4648     Xfake_blankB,                       FALSE,  FALSE,
4649     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
4650   },
4651   {
4652     Xfake_grass,                        TRUE,   FALSE,
4653     EL_EMC_FAKE_GRASS,                  -1, -1
4654   },
4655   {
4656     Xfake_grassB,                       FALSE,  FALSE,
4657     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
4658   },
4659   {
4660     Xfake_door_1,                       TRUE,   FALSE,
4661     EL_EM_GATE_1_GRAY,                  -1, -1
4662   },
4663   {
4664     Xfake_door_2,                       TRUE,   FALSE,
4665     EL_EM_GATE_2_GRAY,                  -1, -1
4666   },
4667   {
4668     Xfake_door_3,                       TRUE,   FALSE,
4669     EL_EM_GATE_3_GRAY,                  -1, -1
4670   },
4671   {
4672     Xfake_door_4,                       TRUE,   FALSE,
4673     EL_EM_GATE_4_GRAY,                  -1, -1
4674   },
4675   {
4676     Xfake_door_5,                       TRUE,   FALSE,
4677     EL_EMC_GATE_5_GRAY,                 -1, -1
4678   },
4679   {
4680     Xfake_door_6,                       TRUE,   FALSE,
4681     EL_EMC_GATE_6_GRAY,                 -1, -1
4682   },
4683   {
4684     Xfake_door_7,                       TRUE,   FALSE,
4685     EL_EMC_GATE_7_GRAY,                 -1, -1
4686   },
4687   {
4688     Xfake_door_8,                       TRUE,   FALSE,
4689     EL_EMC_GATE_8_GRAY,                 -1, -1
4690   },
4691   {
4692     Xfake_acid_1,                       TRUE,   FALSE,
4693     EL_EMC_FAKE_ACID,                   -1, -1
4694   },
4695   {
4696     Xfake_acid_2,                       FALSE,  FALSE,
4697     EL_EMC_FAKE_ACID,                   -1, -1
4698   },
4699   {
4700     Xfake_acid_3,                       FALSE,  FALSE,
4701     EL_EMC_FAKE_ACID,                   -1, -1
4702   },
4703   {
4704     Xfake_acid_4,                       FALSE,  FALSE,
4705     EL_EMC_FAKE_ACID,                   -1, -1
4706   },
4707   {
4708     Xfake_acid_5,                       FALSE,  FALSE,
4709     EL_EMC_FAKE_ACID,                   -1, -1
4710   },
4711   {
4712     Xfake_acid_6,                       FALSE,  FALSE,
4713     EL_EMC_FAKE_ACID,                   -1, -1
4714   },
4715   {
4716     Xfake_acid_7,                       FALSE,  FALSE,
4717     EL_EMC_FAKE_ACID,                   -1, -1
4718   },
4719   {
4720     Xfake_acid_8,                       FALSE,  FALSE,
4721     EL_EMC_FAKE_ACID,                   -1, -1
4722   },
4723   {
4724     Xsteel_1,                           TRUE,   FALSE,
4725     EL_STEELWALL,                       -1, -1
4726   },
4727   {
4728     Xsteel_2,                           TRUE,   FALSE,
4729     EL_EMC_STEELWALL_2,                 -1, -1
4730   },
4731   {
4732     Xsteel_3,                           TRUE,   FALSE,
4733     EL_EMC_STEELWALL_3,                 -1, -1
4734   },
4735   {
4736     Xsteel_4,                           TRUE,   FALSE,
4737     EL_EMC_STEELWALL_4,                 -1, -1
4738   },
4739   {
4740     Xwall_1,                            TRUE,   FALSE,
4741     EL_WALL,                            -1, -1
4742   },
4743   {
4744     Xwall_2,                            TRUE,   FALSE,
4745     EL_EMC_WALL_14,                     -1, -1
4746   },
4747   {
4748     Xwall_3,                            TRUE,   FALSE,
4749     EL_EMC_WALL_15,                     -1, -1
4750   },
4751   {
4752     Xwall_4,                            TRUE,   FALSE,
4753     EL_EMC_WALL_16,                     -1, -1
4754   },
4755   {
4756     Xround_wall_1,                      TRUE,   FALSE,
4757     EL_WALL_SLIPPERY,                   -1, -1
4758   },
4759   {
4760     Xround_wall_2,                      TRUE,   FALSE,
4761     EL_EMC_WALL_SLIPPERY_2,             -1, -1
4762   },
4763   {
4764     Xround_wall_3,                      TRUE,   FALSE,
4765     EL_EMC_WALL_SLIPPERY_3,             -1, -1
4766   },
4767   {
4768     Xround_wall_4,                      TRUE,   FALSE,
4769     EL_EMC_WALL_SLIPPERY_4,             -1, -1
4770   },
4771   {
4772     Xdecor_1,                           TRUE,   FALSE,
4773     EL_EMC_WALL_8,                      -1, -1
4774   },
4775   {
4776     Xdecor_2,                           TRUE,   FALSE,
4777     EL_EMC_WALL_6,                      -1, -1
4778   },
4779   {
4780     Xdecor_3,                           TRUE,   FALSE,
4781     EL_EMC_WALL_4,                      -1, -1
4782   },
4783   {
4784     Xdecor_4,                           TRUE,   FALSE,
4785     EL_EMC_WALL_7,                      -1, -1
4786   },
4787   {
4788     Xdecor_5,                           TRUE,   FALSE,
4789     EL_EMC_WALL_5,                      -1, -1
4790   },
4791   {
4792     Xdecor_6,                           TRUE,   FALSE,
4793     EL_EMC_WALL_9,                      -1, -1
4794   },
4795   {
4796     Xdecor_7,                           TRUE,   FALSE,
4797     EL_EMC_WALL_10,                     -1, -1
4798   },
4799   {
4800     Xdecor_8,                           TRUE,   FALSE,
4801     EL_EMC_WALL_1,                      -1, -1
4802   },
4803   {
4804     Xdecor_9,                           TRUE,   FALSE,
4805     EL_EMC_WALL_2,                      -1, -1
4806   },
4807   {
4808     Xdecor_10,                          TRUE,   FALSE,
4809     EL_EMC_WALL_3,                      -1, -1
4810   },
4811   {
4812     Xdecor_11,                          TRUE,   FALSE,
4813     EL_EMC_WALL_11,                     -1, -1
4814   },
4815   {
4816     Xdecor_12,                          TRUE,   FALSE,
4817     EL_EMC_WALL_12,                     -1, -1
4818   },
4819   {
4820     Xalpha_0,                           TRUE,   FALSE,
4821     EL_CHAR('0'),                       -1, -1
4822   },
4823   {
4824     Xalpha_1,                           TRUE,   FALSE,
4825     EL_CHAR('1'),                       -1, -1
4826   },
4827   {
4828     Xalpha_2,                           TRUE,   FALSE,
4829     EL_CHAR('2'),                       -1, -1
4830   },
4831   {
4832     Xalpha_3,                           TRUE,   FALSE,
4833     EL_CHAR('3'),                       -1, -1
4834   },
4835   {
4836     Xalpha_4,                           TRUE,   FALSE,
4837     EL_CHAR('4'),                       -1, -1
4838   },
4839   {
4840     Xalpha_5,                           TRUE,   FALSE,
4841     EL_CHAR('5'),                       -1, -1
4842   },
4843   {
4844     Xalpha_6,                           TRUE,   FALSE,
4845     EL_CHAR('6'),                       -1, -1
4846   },
4847   {
4848     Xalpha_7,                           TRUE,   FALSE,
4849     EL_CHAR('7'),                       -1, -1
4850   },
4851   {
4852     Xalpha_8,                           TRUE,   FALSE,
4853     EL_CHAR('8'),                       -1, -1
4854   },
4855   {
4856     Xalpha_9,                           TRUE,   FALSE,
4857     EL_CHAR('9'),                       -1, -1
4858   },
4859   {
4860     Xalpha_excla,                       TRUE,   FALSE,
4861     EL_CHAR('!'),                       -1, -1
4862   },
4863   {
4864     Xalpha_quote,                       TRUE,   FALSE,
4865     EL_CHAR('"'),                       -1, -1
4866   },
4867   {
4868     Xalpha_comma,                       TRUE,   FALSE,
4869     EL_CHAR(','),                       -1, -1
4870   },
4871   {
4872     Xalpha_minus,                       TRUE,   FALSE,
4873     EL_CHAR('-'),                       -1, -1
4874   },
4875   {
4876     Xalpha_perio,                       TRUE,   FALSE,
4877     EL_CHAR('.'),                       -1, -1
4878   },
4879   {
4880     Xalpha_colon,                       TRUE,   FALSE,
4881     EL_CHAR(':'),                       -1, -1
4882   },
4883   {
4884     Xalpha_quest,                       TRUE,   FALSE,
4885     EL_CHAR('?'),                       -1, -1
4886   },
4887   {
4888     Xalpha_a,                           TRUE,   FALSE,
4889     EL_CHAR('A'),                       -1, -1
4890   },
4891   {
4892     Xalpha_b,                           TRUE,   FALSE,
4893     EL_CHAR('B'),                       -1, -1
4894   },
4895   {
4896     Xalpha_c,                           TRUE,   FALSE,
4897     EL_CHAR('C'),                       -1, -1
4898   },
4899   {
4900     Xalpha_d,                           TRUE,   FALSE,
4901     EL_CHAR('D'),                       -1, -1
4902   },
4903   {
4904     Xalpha_e,                           TRUE,   FALSE,
4905     EL_CHAR('E'),                       -1, -1
4906   },
4907   {
4908     Xalpha_f,                           TRUE,   FALSE,
4909     EL_CHAR('F'),                       -1, -1
4910   },
4911   {
4912     Xalpha_g,                           TRUE,   FALSE,
4913     EL_CHAR('G'),                       -1, -1
4914   },
4915   {
4916     Xalpha_h,                           TRUE,   FALSE,
4917     EL_CHAR('H'),                       -1, -1
4918   },
4919   {
4920     Xalpha_i,                           TRUE,   FALSE,
4921     EL_CHAR('I'),                       -1, -1
4922   },
4923   {
4924     Xalpha_j,                           TRUE,   FALSE,
4925     EL_CHAR('J'),                       -1, -1
4926   },
4927   {
4928     Xalpha_k,                           TRUE,   FALSE,
4929     EL_CHAR('K'),                       -1, -1
4930   },
4931   {
4932     Xalpha_l,                           TRUE,   FALSE,
4933     EL_CHAR('L'),                       -1, -1
4934   },
4935   {
4936     Xalpha_m,                           TRUE,   FALSE,
4937     EL_CHAR('M'),                       -1, -1
4938   },
4939   {
4940     Xalpha_n,                           TRUE,   FALSE,
4941     EL_CHAR('N'),                       -1, -1
4942   },
4943   {
4944     Xalpha_o,                           TRUE,   FALSE,
4945     EL_CHAR('O'),                       -1, -1
4946   },
4947   {
4948     Xalpha_p,                           TRUE,   FALSE,
4949     EL_CHAR('P'),                       -1, -1
4950   },
4951   {
4952     Xalpha_q,                           TRUE,   FALSE,
4953     EL_CHAR('Q'),                       -1, -1
4954   },
4955   {
4956     Xalpha_r,                           TRUE,   FALSE,
4957     EL_CHAR('R'),                       -1, -1
4958   },
4959   {
4960     Xalpha_s,                           TRUE,   FALSE,
4961     EL_CHAR('S'),                       -1, -1
4962   },
4963   {
4964     Xalpha_t,                           TRUE,   FALSE,
4965     EL_CHAR('T'),                       -1, -1
4966   },
4967   {
4968     Xalpha_u,                           TRUE,   FALSE,
4969     EL_CHAR('U'),                       -1, -1
4970   },
4971   {
4972     Xalpha_v,                           TRUE,   FALSE,
4973     EL_CHAR('V'),                       -1, -1
4974   },
4975   {
4976     Xalpha_w,                           TRUE,   FALSE,
4977     EL_CHAR('W'),                       -1, -1
4978   },
4979   {
4980     Xalpha_x,                           TRUE,   FALSE,
4981     EL_CHAR('X'),                       -1, -1
4982   },
4983   {
4984     Xalpha_y,                           TRUE,   FALSE,
4985     EL_CHAR('Y'),                       -1, -1
4986   },
4987   {
4988     Xalpha_z,                           TRUE,   FALSE,
4989     EL_CHAR('Z'),                       -1, -1
4990   },
4991   {
4992     Xalpha_arrow_e,                     TRUE,   FALSE,
4993     EL_CHAR('>'),                       -1, -1
4994   },
4995   {
4996     Xalpha_arrow_w,                     TRUE,   FALSE,
4997     EL_CHAR('<'),                       -1, -1
4998   },
4999   {
5000     Xalpha_copyr,                       TRUE,   FALSE,
5001     EL_CHAR('©'),                       -1, -1
5002   },
5003
5004   {
5005     Xboom_bug,                          FALSE,  FALSE,
5006     EL_BUG,                             ACTION_EXPLODING, -1
5007   },
5008   {
5009     Xboom_bomb,                         FALSE,  FALSE,
5010     EL_BOMB,                            ACTION_EXPLODING, -1
5011   },
5012   {
5013     Xboom_android,                      FALSE,  FALSE,
5014     EL_EMC_ANDROID,                     ACTION_OTHER, -1
5015   },
5016   {
5017     Xboom_1,                            FALSE,  FALSE,
5018     EL_DEFAULT,                         ACTION_EXPLODING, -1
5019   },
5020   {
5021     Xboom_2,                            FALSE,  FALSE,
5022     EL_DEFAULT,                         ACTION_EXPLODING, -1
5023   },
5024   {
5025     Znormal,                            FALSE,  FALSE,
5026     EL_EMPTY,                           -1, -1
5027   },
5028   {
5029     Zdynamite,                          FALSE,  FALSE,
5030     EL_EMPTY,                           -1, -1
5031   },
5032   {
5033     Zplayer,                            FALSE,  FALSE,
5034     EL_EMPTY,                           -1, -1
5035   },
5036   {
5037     ZBORDER,                            FALSE,  FALSE,
5038     EL_EMPTY,                           -1, -1
5039   },
5040
5041   {
5042     -1,                                 FALSE,  FALSE,
5043     -1,                                 -1, -1
5044   }
5045 };
5046
5047 static struct Mapping_EM_to_RND_player
5048 {
5049   int action_em;
5050   int player_nr;
5051
5052   int element_rnd;
5053   int action;
5054   int direction;
5055 }
5056 em_player_mapping_list[] =
5057 {
5058   {
5059     SPR_walk + 0,                       0,
5060     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
5061   },
5062   {
5063     SPR_walk + 1,                       0,
5064     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
5065   },
5066   {
5067     SPR_walk + 2,                       0,
5068     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
5069   },
5070   {
5071     SPR_walk + 3,                       0,
5072     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
5073   },
5074   {
5075     SPR_push + 0,                       0,
5076     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
5077   },
5078   {
5079     SPR_push + 1,                       0,
5080     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
5081   },
5082   {
5083     SPR_push + 2,                       0,
5084     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
5085   },
5086   {
5087     SPR_push + 3,                       0,
5088     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
5089   },
5090   {
5091     SPR_spray + 0,                      0,
5092     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
5093   },
5094   {
5095     SPR_spray + 1,                      0,
5096     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
5097   },
5098   {
5099     SPR_spray + 2,                      0,
5100     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
5101   },
5102   {
5103     SPR_spray + 3,                      0,
5104     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
5105   },
5106   {
5107     SPR_walk + 0,                       1,
5108     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
5109   },
5110   {
5111     SPR_walk + 1,                       1,
5112     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
5113   },
5114   {
5115     SPR_walk + 2,                       1,
5116     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
5117   },
5118   {
5119     SPR_walk + 3,                       1,
5120     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
5121   },
5122   {
5123     SPR_push + 0,                       1,
5124     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
5125   },
5126   {
5127     SPR_push + 1,                       1,
5128     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
5129   },
5130   {
5131     SPR_push + 2,                       1,
5132     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
5133   },
5134   {
5135     SPR_push + 3,                       1,
5136     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
5137   },
5138   {
5139     SPR_spray + 0,                      1,
5140     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
5141   },
5142   {
5143     SPR_spray + 1,                      1,
5144     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
5145   },
5146   {
5147     SPR_spray + 2,                      1,
5148     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
5149   },
5150   {
5151     SPR_spray + 3,                      1,
5152     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
5153   },
5154   {
5155     SPR_still,                          0,
5156     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
5157   },
5158   {
5159     SPR_still,                          1,
5160     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
5161   },
5162   {
5163     SPR_walk + 0,                       2,
5164     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
5165   },
5166   {
5167     SPR_walk + 1,                       2,
5168     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
5169   },
5170   {
5171     SPR_walk + 2,                       2,
5172     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
5173   },
5174   {
5175     SPR_walk + 3,                       2,
5176     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
5177   },
5178   {
5179     SPR_push + 0,                       2,
5180     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
5181   },
5182   {
5183     SPR_push + 1,                       2,
5184     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
5185   },
5186   {
5187     SPR_push + 2,                       2,
5188     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
5189   },
5190   {
5191     SPR_push + 3,                       2,
5192     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
5193   },
5194   {
5195     SPR_spray + 0,                      2,
5196     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
5197   },
5198   {
5199     SPR_spray + 1,                      2,
5200     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
5201   },
5202   {
5203     SPR_spray + 2,                      2,
5204     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
5205   },
5206   {
5207     SPR_spray + 3,                      2,
5208     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
5209   },
5210   {
5211     SPR_walk + 0,                       3,
5212     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
5213   },
5214   {
5215     SPR_walk + 1,                       3,
5216     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
5217   },
5218   {
5219     SPR_walk + 2,                       3,
5220     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
5221   },
5222   {
5223     SPR_walk + 3,                       3,
5224     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
5225   },
5226   {
5227     SPR_push + 0,                       3,
5228     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
5229   },
5230   {
5231     SPR_push + 1,                       3,
5232     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
5233   },
5234   {
5235     SPR_push + 2,                       3,
5236     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
5237   },
5238   {
5239     SPR_push + 3,                       3,
5240     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
5241   },
5242   {
5243     SPR_spray + 0,                      3,
5244     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
5245   },
5246   {
5247     SPR_spray + 1,                      3,
5248     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
5249   },
5250   {
5251     SPR_spray + 2,                      3,
5252     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
5253   },
5254   {
5255     SPR_spray + 3,                      3,
5256     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
5257   },
5258   {
5259     SPR_still,                          2,
5260     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
5261   },
5262   {
5263     SPR_still,                          3,
5264     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
5265   },
5266
5267   {
5268     -1,                                 -1,
5269     -1,                                 -1, -1
5270   }
5271 };
5272
5273 int map_element_RND_to_EM(int element_rnd)
5274 {
5275   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
5276   static boolean mapping_initialized = FALSE;
5277
5278   if (!mapping_initialized)
5279   {
5280     int i;
5281
5282     /* return "Xalpha_quest" for all undefined elements in mapping array */
5283     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
5284       mapping_RND_to_EM[i] = Xalpha_quest;
5285
5286     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
5287       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
5288         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
5289           em_object_mapping_list[i].element_em;
5290
5291     mapping_initialized = TRUE;
5292   }
5293
5294   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
5295     return mapping_RND_to_EM[element_rnd];
5296
5297   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
5298
5299   return EL_UNKNOWN;
5300 }
5301
5302 int map_element_EM_to_RND(int element_em)
5303 {
5304   static unsigned short mapping_EM_to_RND[TILE_MAX];
5305   static boolean mapping_initialized = FALSE;
5306
5307   if (!mapping_initialized)
5308   {
5309     int i;
5310
5311     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
5312     for (i = 0; i < TILE_MAX; i++)
5313       mapping_EM_to_RND[i] = EL_UNKNOWN;
5314
5315     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
5316       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
5317         em_object_mapping_list[i].element_rnd;
5318
5319     mapping_initialized = TRUE;
5320   }
5321
5322   if (element_em >= 0 && element_em < TILE_MAX)
5323     return mapping_EM_to_RND[element_em];
5324
5325   Error(ERR_WARN, "invalid EM level element %d", element_em);
5326
5327   return EL_UNKNOWN;
5328 }
5329
5330 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
5331 {
5332   struct LevelInfo_EM *level_em = level->native_em_level;
5333   struct LEVEL *lev = level_em->lev;
5334   int i, j;
5335
5336   for (i = 0; i < TILE_MAX; i++)
5337     lev->android_array[i] = Xblank;
5338
5339   for (i = 0; i < level->num_android_clone_elements; i++)
5340   {
5341     int element_rnd = level->android_clone_element[i];
5342     int element_em = map_element_RND_to_EM(element_rnd);
5343
5344     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
5345       if (em_object_mapping_list[j].element_rnd == element_rnd)
5346         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
5347   }
5348 }
5349
5350 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
5351 {
5352   struct LevelInfo_EM *level_em = level->native_em_level;
5353   struct LEVEL *lev = level_em->lev;
5354   int i, j;
5355
5356   level->num_android_clone_elements = 0;
5357
5358   for (i = 0; i < TILE_MAX; i++)
5359   {
5360     int element_em = lev->android_array[i];
5361     int element_rnd;
5362     boolean element_found = FALSE;
5363
5364     if (element_em == Xblank)
5365       continue;
5366
5367     element_rnd = map_element_EM_to_RND(element_em);
5368
5369     for (j = 0; j < level->num_android_clone_elements; j++)
5370       if (level->android_clone_element[j] == element_rnd)
5371         element_found = TRUE;
5372
5373     if (!element_found)
5374     {
5375       level->android_clone_element[level->num_android_clone_elements++] =
5376         element_rnd;
5377
5378       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
5379         break;
5380     }
5381   }
5382
5383   if (level->num_android_clone_elements == 0)
5384   {
5385     level->num_android_clone_elements = 1;
5386     level->android_clone_element[0] = EL_EMPTY;
5387   }
5388 }
5389
5390 int map_direction_RND_to_EM(int direction)
5391 {
5392   return (direction == MV_UP    ? 0 :
5393           direction == MV_RIGHT ? 1 :
5394           direction == MV_DOWN  ? 2 :
5395           direction == MV_LEFT  ? 3 :
5396           -1);
5397 }
5398
5399 int map_direction_EM_to_RND(int direction)
5400 {
5401   return (direction == 0 ? MV_UP    :
5402           direction == 1 ? MV_RIGHT :
5403           direction == 2 ? MV_DOWN  :
5404           direction == 3 ? MV_LEFT  :
5405           MV_NONE);
5406 }
5407
5408 int get_next_element(int element)
5409 {
5410   switch (element)
5411   {
5412     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
5413     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
5414     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
5415     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
5416     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
5417     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
5418     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
5419     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
5420     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
5421     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
5422     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
5423
5424     default:                            return element;
5425   }
5426 }
5427
5428 #if 0
5429 int el_act_dir2img(int element, int action, int direction)
5430 {
5431   element = GFX_ELEMENT(element);
5432
5433   if (direction == MV_NONE)
5434     return element_info[element].graphic[action];
5435
5436   direction = MV_DIR_TO_BIT(direction);
5437
5438   return element_info[element].direction_graphic[action][direction];
5439 }
5440 #else
5441 int el_act_dir2img(int element, int action, int direction)
5442 {
5443   element = GFX_ELEMENT(element);
5444   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
5445
5446   /* direction_graphic[][] == graphic[] for undefined direction graphics */
5447   return element_info[element].direction_graphic[action][direction];
5448 }
5449 #endif
5450
5451 #if 0
5452 static int el_act_dir2crm(int element, int action, int direction)
5453 {
5454   element = GFX_ELEMENT(element);
5455
5456   if (direction == MV_NONE)
5457     return element_info[element].crumbled[action];
5458
5459   direction = MV_DIR_TO_BIT(direction);
5460
5461   return element_info[element].direction_crumbled[action][direction];
5462 }
5463 #else
5464 static int el_act_dir2crm(int element, int action, int direction)
5465 {
5466   element = GFX_ELEMENT(element);
5467   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
5468
5469   /* direction_graphic[][] == graphic[] for undefined direction graphics */
5470   return element_info[element].direction_crumbled[action][direction];
5471 }
5472 #endif
5473
5474 int el_act2img(int element, int action)
5475 {
5476   element = GFX_ELEMENT(element);
5477
5478   return element_info[element].graphic[action];
5479 }
5480
5481 int el_act2crm(int element, int action)
5482 {
5483   element = GFX_ELEMENT(element);
5484
5485   return element_info[element].crumbled[action];
5486 }
5487
5488 int el_dir2img(int element, int direction)
5489 {
5490   element = GFX_ELEMENT(element);
5491
5492   return el_act_dir2img(element, ACTION_DEFAULT, direction);
5493 }
5494
5495 int el2baseimg(int element)
5496 {
5497   return element_info[element].graphic[ACTION_DEFAULT];
5498 }
5499
5500 int el2img(int element)
5501 {
5502   element = GFX_ELEMENT(element);
5503
5504   return element_info[element].graphic[ACTION_DEFAULT];
5505 }
5506
5507 int el2edimg(int element)
5508 {
5509   element = GFX_ELEMENT(element);
5510
5511   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
5512 }
5513
5514 int el2preimg(int element)
5515 {
5516   element = GFX_ELEMENT(element);
5517
5518   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
5519 }
5520
5521 int font2baseimg(int font_nr)
5522 {
5523   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
5524 }
5525
5526 int getNumActivePlayers_EM()
5527 {
5528   int num_players = 0;
5529   int i;
5530
5531   if (!tape.playing)
5532     return -1;
5533
5534   for (i = 0; i < MAX_PLAYERS; i++)
5535     if (tape.player_participates[i])
5536       num_players++;
5537
5538   return num_players;
5539 }
5540
5541 int getGameFrameDelay_EM(int native_em_game_frame_delay)
5542 {
5543   int game_frame_delay_value;
5544
5545   game_frame_delay_value =
5546     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
5547      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
5548      GameFrameDelay);
5549
5550   if (tape.playing && tape.warp_forward && !tape.pausing)
5551     game_frame_delay_value = 0;
5552
5553   return game_frame_delay_value;
5554 }
5555
5556 unsigned int InitRND(long seed)
5557 {
5558   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
5559     return InitEngineRandom_EM(seed);
5560   else
5561     return InitEngineRandom_RND(seed);
5562 }
5563
5564 void InitGraphicInfo_EM(void)
5565 {
5566   struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
5567   struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
5568   int i, j, p;
5569
5570 #if DEBUG_EM_GFX
5571   int num_em_gfx_errors = 0;
5572
5573   if (graphic_info_em_object[0][0].bitmap == NULL)
5574   {
5575     /* EM graphics not yet initialized in em_open_all() */
5576
5577     return;
5578   }
5579
5580   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
5581 #endif
5582
5583   /* always start with reliable default values */
5584   for (i = 0; i < TILE_MAX; i++)
5585   {
5586     object_mapping[i].element_rnd = EL_UNKNOWN;
5587     object_mapping[i].is_backside = FALSE;
5588     object_mapping[i].action = ACTION_DEFAULT;
5589     object_mapping[i].direction = MV_NONE;
5590   }
5591
5592   /* always start with reliable default values */
5593   for (p = 0; p < MAX_PLAYERS; p++)
5594   {
5595     for (i = 0; i < SPR_MAX; i++)
5596     {
5597       player_mapping[p][i].element_rnd = EL_UNKNOWN;
5598       player_mapping[p][i].action = ACTION_DEFAULT;
5599       player_mapping[p][i].direction = MV_NONE;
5600     }
5601   }
5602
5603   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
5604   {
5605     int e = em_object_mapping_list[i].element_em;
5606
5607     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
5608     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
5609
5610     if (em_object_mapping_list[i].action != -1)
5611       object_mapping[e].action = em_object_mapping_list[i].action;
5612
5613     if (em_object_mapping_list[i].direction != -1)
5614       object_mapping[e].direction =
5615         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
5616   }
5617
5618   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
5619   {
5620     int a = em_player_mapping_list[i].action_em;
5621     int p = em_player_mapping_list[i].player_nr;
5622
5623     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
5624
5625     if (em_player_mapping_list[i].action != -1)
5626       player_mapping[p][a].action = em_player_mapping_list[i].action;
5627
5628     if (em_player_mapping_list[i].direction != -1)
5629       player_mapping[p][a].direction =
5630         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
5631   }
5632
5633   for (i = 0; i < TILE_MAX; i++)
5634   {
5635     int element = object_mapping[i].element_rnd;
5636     int action = object_mapping[i].action;
5637     int direction = object_mapping[i].direction;
5638     boolean is_backside = object_mapping[i].is_backside;
5639     boolean action_removing = (action == ACTION_DIGGING ||
5640                                action == ACTION_SNAPPING ||
5641                                action == ACTION_COLLECTING);
5642     boolean action_exploding = ((action == ACTION_EXPLODING ||
5643                                  action == ACTION_SMASHED_BY_ROCK ||
5644                                  action == ACTION_SMASHED_BY_SPRING) &&
5645                                 element != EL_DIAMOND);
5646     boolean action_active = (action == ACTION_ACTIVE);
5647     boolean action_other = (action == ACTION_OTHER);
5648
5649     for (j = 0; j < 8; j++)
5650     {
5651       int effective_element = (j > 5 && i == Yacid_splash_eB ? EL_EMPTY :
5652                                j > 5 && i == Yacid_splash_wB ? EL_EMPTY :
5653                                j < 7 ? element :
5654                                i == Xdrip_stretch ? element :
5655                                i == Xdrip_stretchB ? element :
5656                                i == Ydrip_s1 ? element :
5657                                i == Ydrip_s1B ? element :
5658                                i == Xball_1B ? element :
5659                                i == Xball_2 ? element :
5660                                i == Xball_2B ? element :
5661                                i == Yball_eat ? element :
5662                                i == Ykey_1_eat ? element :
5663                                i == Ykey_2_eat ? element :
5664                                i == Ykey_3_eat ? element :
5665                                i == Ykey_4_eat ? element :
5666                                i == Ykey_5_eat ? element :
5667                                i == Ykey_6_eat ? element :
5668                                i == Ykey_7_eat ? element :
5669                                i == Ykey_8_eat ? element :
5670                                i == Ylenses_eat ? element :
5671                                i == Ymagnify_eat ? element :
5672                                i == Ygrass_eat ? element :
5673                                i == Ydirt_eat ? element :
5674                                i == Yemerald_stone ? EL_EMERALD :
5675                                i == Ydiamond_stone ? EL_ROCK :
5676                                i == Xsand_stonein_1 ? element :
5677                                i == Xsand_stonein_2 ? element :
5678                                i == Xsand_stonein_3 ? element :
5679                                i == Xsand_stonein_4 ? element :
5680                                is_backside ? EL_EMPTY :
5681                                action_removing ? EL_EMPTY :
5682                                element);
5683       int effective_action = (j < 7 ? action :
5684                               i == Xdrip_stretch ? action :
5685                               i == Xdrip_stretchB ? action :
5686                               i == Ydrip_s1 ? action :
5687                               i == Ydrip_s1B ? action :
5688                               i == Xball_1B ? action :
5689                               i == Xball_2 ? action :
5690                               i == Xball_2B ? action :
5691                               i == Yball_eat ? action :
5692                               i == Ykey_1_eat ? action :
5693                               i == Ykey_2_eat ? action :
5694                               i == Ykey_3_eat ? action :
5695                               i == Ykey_4_eat ? action :
5696                               i == Ykey_5_eat ? action :
5697                               i == Ykey_6_eat ? action :
5698                               i == Ykey_7_eat ? action :
5699                               i == Ykey_8_eat ? action :
5700                               i == Ylenses_eat ? action :
5701                               i == Ymagnify_eat ? action :
5702                               i == Ygrass_eat ? action :
5703                               i == Ydirt_eat ? action :
5704                               i == Xsand_stonein_1 ? action :
5705                               i == Xsand_stonein_2 ? action :
5706                               i == Xsand_stonein_3 ? action :
5707                               i == Xsand_stonein_4 ? action :
5708                               i == Xsand_stoneout_1 ? action :
5709                               i == Xsand_stoneout_2 ? action :
5710                               i == Xboom_android ? ACTION_EXPLODING :
5711                               action_exploding ? ACTION_EXPLODING :
5712                               action_active ? action :
5713                               action_other ? action :
5714                               ACTION_DEFAULT);
5715       int graphic = (el_act_dir2img(effective_element, effective_action,
5716                                     direction));
5717       int crumbled = (el_act_dir2crm(effective_element, effective_action,
5718                                      direction));
5719       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
5720       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
5721       boolean has_action_graphics = (graphic != base_graphic);
5722       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
5723       struct GraphicInfo *g = &graphic_info[graphic];
5724       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
5725       Bitmap *src_bitmap;
5726       int src_x, src_y;
5727       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
5728       boolean special_animation = (action != ACTION_DEFAULT &&
5729                                    g->anim_frames == 3 &&
5730                                    g->anim_delay == 2 &&
5731                                    g->anim_mode & ANIM_LINEAR);
5732       int sync_frame = (i == Xdrip_stretch ? 7 :
5733                         i == Xdrip_stretchB ? 7 :
5734                         i == Ydrip_s2 ? j + 8 :
5735                         i == Ydrip_s2B ? j + 8 :
5736                         i == Xacid_1 ? 0 :
5737                         i == Xacid_2 ? 10 :
5738                         i == Xacid_3 ? 20 :
5739                         i == Xacid_4 ? 30 :
5740                         i == Xacid_5 ? 40 :
5741                         i == Xacid_6 ? 50 :
5742                         i == Xacid_7 ? 60 :
5743                         i == Xacid_8 ? 70 :
5744                         i == Xfake_acid_1 ? 0 :
5745                         i == Xfake_acid_2 ? 10 :
5746                         i == Xfake_acid_3 ? 20 :
5747                         i == Xfake_acid_4 ? 30 :
5748                         i == Xfake_acid_5 ? 40 :
5749                         i == Xfake_acid_6 ? 50 :
5750                         i == Xfake_acid_7 ? 60 :
5751                         i == Xfake_acid_8 ? 70 :
5752                         i == Xball_2 ? 7 :
5753                         i == Xball_2B ? j + 8 :
5754                         i == Yball_eat ? j + 1 :
5755                         i == Ykey_1_eat ? j + 1 :
5756                         i == Ykey_2_eat ? j + 1 :
5757                         i == Ykey_3_eat ? j + 1 :
5758                         i == Ykey_4_eat ? j + 1 :
5759                         i == Ykey_5_eat ? j + 1 :
5760                         i == Ykey_6_eat ? j + 1 :
5761                         i == Ykey_7_eat ? j + 1 :
5762                         i == Ykey_8_eat ? j + 1 :
5763                         i == Ylenses_eat ? j + 1 :
5764                         i == Ymagnify_eat ? j + 1 :
5765                         i == Ygrass_eat ? j + 1 :
5766                         i == Ydirt_eat ? j + 1 :
5767                         i == Xamoeba_1 ? 0 :
5768                         i == Xamoeba_2 ? 1 :
5769                         i == Xamoeba_3 ? 2 :
5770                         i == Xamoeba_4 ? 3 :
5771                         i == Xamoeba_5 ? 0 :
5772                         i == Xamoeba_6 ? 1 :
5773                         i == Xamoeba_7 ? 2 :
5774                         i == Xamoeba_8 ? 3 :
5775                         i == Xexit_2 ? j + 8 :
5776                         i == Xexit_3 ? j + 16 :
5777                         i == Xdynamite_1 ? 0 :
5778                         i == Xdynamite_2 ? 8 :
5779                         i == Xdynamite_3 ? 16 :
5780                         i == Xdynamite_4 ? 24 :
5781                         i == Xsand_stonein_1 ? j + 1 :
5782                         i == Xsand_stonein_2 ? j + 9 :
5783                         i == Xsand_stonein_3 ? j + 17 :
5784                         i == Xsand_stonein_4 ? j + 25 :
5785                         i == Xsand_stoneout_1 && j == 0 ? 0 :
5786                         i == Xsand_stoneout_1 && j == 1 ? 0 :
5787                         i == Xsand_stoneout_1 && j == 2 ? 1 :
5788                         i == Xsand_stoneout_1 && j == 3 ? 2 :
5789                         i == Xsand_stoneout_1 && j == 4 ? 2 :
5790                         i == Xsand_stoneout_1 && j == 5 ? 3 :
5791                         i == Xsand_stoneout_1 && j == 6 ? 4 :
5792                         i == Xsand_stoneout_1 && j == 7 ? 4 :
5793                         i == Xsand_stoneout_2 && j == 0 ? 5 :
5794                         i == Xsand_stoneout_2 && j == 1 ? 6 :
5795                         i == Xsand_stoneout_2 && j == 2 ? 7 :
5796                         i == Xsand_stoneout_2 && j == 3 ? 8 :
5797                         i == Xsand_stoneout_2 && j == 4 ? 9 :
5798                         i == Xsand_stoneout_2 && j == 5 ? 11 :
5799                         i == Xsand_stoneout_2 && j == 6 ? 13 :
5800                         i == Xsand_stoneout_2 && j == 7 ? 15 :
5801                         i == Xboom_bug && j == 1 ? 2 :
5802                         i == Xboom_bug && j == 2 ? 2 :
5803                         i == Xboom_bug && j == 3 ? 4 :
5804                         i == Xboom_bug && j == 4 ? 4 :
5805                         i == Xboom_bug && j == 5 ? 2 :
5806                         i == Xboom_bug && j == 6 ? 2 :
5807                         i == Xboom_bug && j == 7 ? 0 :
5808                         i == Xboom_bomb && j == 1 ? 2 :
5809                         i == Xboom_bomb && j == 2 ? 2 :
5810                         i == Xboom_bomb && j == 3 ? 4 :
5811                         i == Xboom_bomb && j == 4 ? 4 :
5812                         i == Xboom_bomb && j == 5 ? 2 :
5813                         i == Xboom_bomb && j == 6 ? 2 :
5814                         i == Xboom_bomb && j == 7 ? 0 :
5815                         i == Xboom_android && j == 7 ? 6 :
5816                         i == Xboom_1 && j == 1 ? 2 :
5817                         i == Xboom_1 && j == 2 ? 2 :
5818                         i == Xboom_1 && j == 3 ? 4 :
5819                         i == Xboom_1 && j == 4 ? 4 :
5820                         i == Xboom_1 && j == 5 ? 6 :
5821                         i == Xboom_1 && j == 6 ? 6 :
5822                         i == Xboom_1 && j == 7 ? 8 :
5823                         i == Xboom_2 && j == 0 ? 8 :
5824                         i == Xboom_2 && j == 1 ? 8 :
5825                         i == Xboom_2 && j == 2 ? 10 :
5826                         i == Xboom_2 && j == 3 ? 10 :
5827                         i == Xboom_2 && j == 4 ? 10 :
5828                         i == Xboom_2 && j == 5 ? 12 :
5829                         i == Xboom_2 && j == 6 ? 12 :
5830                         i == Xboom_2 && j == 7 ? 12 :
5831                         special_animation && j == 4 ? 3 :
5832                         effective_action != action ? 0 :
5833                         j);
5834
5835 #if DEBUG_EM_GFX
5836       Bitmap *debug_bitmap = g_em->bitmap;
5837       int debug_src_x = g_em->src_x;
5838       int debug_src_y = g_em->src_y;
5839 #endif
5840
5841       int frame = getAnimationFrame(g->anim_frames,
5842                                     g->anim_delay,
5843                                     g->anim_mode,
5844                                     g->anim_start_frame,
5845                                     sync_frame);
5846
5847       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
5848                           g->double_movement && is_backside);
5849
5850       g_em->bitmap = src_bitmap;
5851       g_em->src_x = src_x;
5852       g_em->src_y = src_y;
5853       g_em->src_offset_x = 0;
5854       g_em->src_offset_y = 0;
5855       g_em->dst_offset_x = 0;
5856       g_em->dst_offset_y = 0;
5857       g_em->width  = TILEX;
5858       g_em->height = TILEY;
5859
5860       g_em->crumbled_bitmap = NULL;
5861       g_em->crumbled_src_x = 0;
5862       g_em->crumbled_src_y = 0;
5863       g_em->crumbled_border_size = 0;
5864
5865       g_em->has_crumbled_graphics = FALSE;
5866       g_em->preserve_background = FALSE;
5867
5868 #if 0
5869       if (has_crumbled_graphics && crumbled == IMG_EMPTY_SPACE)
5870         printf("::: empty crumbled: %d [%s], %d, %d\n",
5871                effective_element, element_info[effective_element].token_name,
5872                effective_action, direction);
5873 #endif
5874
5875       /* if element can be crumbled, but certain action graphics are just empty
5876          space (like snapping sand with the original R'n'D graphics), do not
5877          treat these empty space graphics as crumbled graphics in EMC engine */
5878       if (has_crumbled_graphics && crumbled != IMG_EMPTY_SPACE)
5879       {
5880         getGraphicSource(crumbled, frame, &src_bitmap, &src_x, &src_y);
5881
5882         g_em->has_crumbled_graphics = TRUE;
5883         g_em->crumbled_bitmap = src_bitmap;
5884         g_em->crumbled_src_x = src_x;
5885         g_em->crumbled_src_y = src_y;
5886         g_em->crumbled_border_size = graphic_info[crumbled].border_size;
5887       }
5888
5889 #if 0
5890       if (element == EL_ROCK &&
5891           effective_action == ACTION_FILLING)
5892         printf("::: has_action_graphics == %d\n", has_action_graphics);
5893 #endif
5894
5895       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
5896                                    effective_action == ACTION_MOVING  ||
5897                                    effective_action == ACTION_PUSHING ||
5898                                    effective_action == ACTION_EATING)) ||
5899           (!has_action_graphics && (effective_action == ACTION_FILLING ||
5900                                     effective_action == ACTION_EMPTYING)))
5901       {
5902         int move_dir =
5903           (effective_action == ACTION_FALLING ||
5904            effective_action == ACTION_FILLING ||
5905            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
5906         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
5907         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
5908         int num_steps = (i == Ydrip_s1  ? 16 :
5909                          i == Ydrip_s1B ? 16 :
5910                          i == Ydrip_s2  ? 16 :
5911                          i == Ydrip_s2B ? 16 :
5912                          i == Xsand_stonein_1 ? 32 :
5913                          i == Xsand_stonein_2 ? 32 :
5914                          i == Xsand_stonein_3 ? 32 :
5915                          i == Xsand_stonein_4 ? 32 :
5916                          i == Xsand_stoneout_1 ? 16 :
5917                          i == Xsand_stoneout_2 ? 16 : 8);
5918         int cx = ABS(dx) * (TILEX / num_steps);
5919         int cy = ABS(dy) * (TILEY / num_steps);
5920         int step_frame = (i == Ydrip_s2         ? j + 8 :
5921                           i == Ydrip_s2B        ? j + 8 :
5922                           i == Xsand_stonein_2  ? j + 8 :
5923                           i == Xsand_stonein_3  ? j + 16 :
5924                           i == Xsand_stonein_4  ? j + 24 :
5925                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
5926         int step = (is_backside ? step_frame : num_steps - step_frame);
5927
5928         if (is_backside)        /* tile where movement starts */
5929         {
5930           if (dx < 0 || dy < 0)
5931           {
5932             g_em->src_offset_x = cx * step;
5933             g_em->src_offset_y = cy * step;
5934           }
5935           else
5936           {
5937             g_em->dst_offset_x = cx * step;
5938             g_em->dst_offset_y = cy * step;
5939           }
5940         }
5941         else                    /* tile where movement ends */
5942         {
5943           if (dx < 0 || dy < 0)
5944           {
5945             g_em->dst_offset_x = cx * step;
5946             g_em->dst_offset_y = cy * step;
5947           }
5948           else
5949           {
5950             g_em->src_offset_x = cx * step;
5951             g_em->src_offset_y = cy * step;
5952           }
5953         }
5954
5955         g_em->width  = TILEX - cx * step;
5956         g_em->height = TILEY - cy * step;
5957       }
5958
5959       /* create unique graphic identifier to decide if tile must be redrawn */
5960       /* bit 31 - 16 (16 bit): EM style graphic
5961          bit 15 - 12 ( 4 bit): EM style frame
5962          bit 11 -  6 ( 6 bit): graphic width
5963          bit  5 -  0 ( 6 bit): graphic height */
5964       g_em->unique_identifier =
5965         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
5966
5967 #if DEBUG_EM_GFX
5968
5969       /* skip check for EMC elements not contained in original EMC artwork */
5970       if (element == EL_EMC_FAKE_ACID)
5971         continue;
5972
5973       if (g_em->bitmap != debug_bitmap ||
5974           g_em->src_x != debug_src_x ||
5975           g_em->src_y != debug_src_y ||
5976           g_em->src_offset_x != 0 ||
5977           g_em->src_offset_y != 0 ||
5978           g_em->dst_offset_x != 0 ||
5979           g_em->dst_offset_y != 0 ||
5980           g_em->width != TILEX ||
5981           g_em->height != TILEY)
5982       {
5983         static int last_i = -1;
5984
5985         if (i != last_i)
5986         {
5987           printf("\n");
5988           last_i = i;
5989         }
5990
5991         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
5992                i, element, element_info[element].token_name,
5993                element_action_info[effective_action].suffix, direction);
5994
5995         if (element != effective_element)
5996           printf(" [%d ('%s')]",
5997                  effective_element,
5998                  element_info[effective_element].token_name);
5999
6000         printf("\n");
6001
6002         if (g_em->bitmap != debug_bitmap)
6003           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
6004                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
6005
6006         if (g_em->src_x != debug_src_x ||
6007             g_em->src_y != debug_src_y)
6008           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
6009                  j, (is_backside ? 'B' : 'F'),
6010                  g_em->src_x, g_em->src_y,
6011                  g_em->src_x / 32, g_em->src_y / 32,
6012                  debug_src_x, debug_src_y,
6013                  debug_src_x / 32, debug_src_y / 32);
6014
6015         if (g_em->src_offset_x != 0 ||
6016             g_em->src_offset_y != 0 ||
6017             g_em->dst_offset_x != 0 ||
6018             g_em->dst_offset_y != 0)
6019           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
6020                  j, is_backside,
6021                  g_em->src_offset_x, g_em->src_offset_y,
6022                  g_em->dst_offset_x, g_em->dst_offset_y);
6023
6024         if (g_em->width != TILEX ||
6025             g_em->height != TILEY)
6026           printf("    %d (%d): size %d,%d should be %d,%d\n",
6027                  j, is_backside,
6028                  g_em->width, g_em->height, TILEX, TILEY);
6029
6030         num_em_gfx_errors++;
6031       }
6032 #endif
6033
6034     }
6035   }
6036
6037   for (i = 0; i < TILE_MAX; i++)
6038   {
6039     for (j = 0; j < 8; j++)
6040     {
6041       int element = object_mapping[i].element_rnd;
6042       int action = object_mapping[i].action;
6043       int direction = object_mapping[i].direction;
6044       boolean is_backside = object_mapping[i].is_backside;
6045       int graphic_action  = el_act_dir2img(element, action, direction);
6046       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
6047
6048       if ((action == ACTION_SMASHED_BY_ROCK ||
6049            action == ACTION_SMASHED_BY_SPRING ||
6050            action == ACTION_EATING) &&
6051           graphic_action == graphic_default)
6052       {
6053         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
6054                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
6055                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
6056                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
6057                  Xspring);
6058
6059         /* no separate animation for "smashed by rock" -- use rock instead */
6060         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
6061         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
6062
6063         g_em->bitmap            = g_xx->bitmap;
6064         g_em->src_x             = g_xx->src_x;
6065         g_em->src_y             = g_xx->src_y;
6066         g_em->src_offset_x      = g_xx->src_offset_x;
6067         g_em->src_offset_y      = g_xx->src_offset_y;
6068         g_em->dst_offset_x      = g_xx->dst_offset_x;
6069         g_em->dst_offset_y      = g_xx->dst_offset_y;
6070         g_em->width             = g_xx->width;
6071         g_em->height            = g_xx->height;
6072         g_em->unique_identifier = g_xx->unique_identifier;
6073
6074         if (!is_backside)
6075           g_em->preserve_background = TRUE;
6076       }
6077     }
6078   }
6079
6080   for (p = 0; p < MAX_PLAYERS; p++)
6081   {
6082     for (i = 0; i < SPR_MAX; i++)
6083     {
6084       int element = player_mapping[p][i].element_rnd;
6085       int action = player_mapping[p][i].action;
6086       int direction = player_mapping[p][i].direction;
6087
6088       for (j = 0; j < 8; j++)
6089       {
6090         int effective_element = element;
6091         int effective_action = action;
6092         int graphic = (direction == MV_NONE ?
6093                        el_act2img(effective_element, effective_action) :
6094                        el_act_dir2img(effective_element, effective_action,
6095                                       direction));
6096         struct GraphicInfo *g = &graphic_info[graphic];
6097         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
6098         Bitmap *src_bitmap;
6099         int src_x, src_y;
6100         int sync_frame = j;
6101
6102 #if DEBUG_EM_GFX
6103         Bitmap *debug_bitmap = g_em->bitmap;
6104         int debug_src_x = g_em->src_x;
6105         int debug_src_y = g_em->src_y;
6106 #endif
6107
6108         int frame = getAnimationFrame(g->anim_frames,
6109                                       g->anim_delay,
6110                                       g->anim_mode,
6111                                       g->anim_start_frame,
6112                                       sync_frame);
6113
6114         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x,&src_y, FALSE);
6115
6116         g_em->bitmap = src_bitmap;
6117         g_em->src_x = src_x;
6118         g_em->src_y = src_y;
6119         g_em->src_offset_x = 0;
6120         g_em->src_offset_y = 0;
6121         g_em->dst_offset_x = 0;
6122         g_em->dst_offset_y = 0;
6123         g_em->width  = TILEX;
6124         g_em->height = TILEY;
6125
6126 #if DEBUG_EM_GFX
6127
6128         /* skip check for EMC elements not contained in original EMC artwork */
6129         if (element == EL_PLAYER_3 ||
6130             element == EL_PLAYER_4)
6131           continue;
6132
6133         if (g_em->bitmap != debug_bitmap ||
6134             g_em->src_x != debug_src_x ||
6135             g_em->src_y != debug_src_y)
6136         {
6137           static int last_i = -1;
6138
6139           if (i != last_i)
6140           {
6141             printf("\n");
6142             last_i = i;
6143           }
6144
6145           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
6146                  p, i, element, element_info[element].token_name,
6147                  element_action_info[effective_action].suffix, direction);
6148
6149           if (element != effective_element)
6150             printf(" [%d ('%s')]",
6151                    effective_element,
6152                    element_info[effective_element].token_name);
6153
6154           printf("\n");
6155
6156           if (g_em->bitmap != debug_bitmap)
6157             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
6158                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
6159
6160           if (g_em->src_x != debug_src_x ||
6161               g_em->src_y != debug_src_y)
6162             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
6163                    j,
6164                    g_em->src_x, g_em->src_y,
6165                    g_em->src_x / 32, g_em->src_y / 32,
6166                    debug_src_x, debug_src_y,
6167                    debug_src_x / 32, debug_src_y / 32);
6168
6169           num_em_gfx_errors++;
6170         }
6171 #endif
6172
6173       }
6174     }
6175   }
6176
6177 #if DEBUG_EM_GFX
6178   printf("\n");
6179   printf("::: [%d errors found]\n", num_em_gfx_errors);
6180
6181   exit(0);
6182 #endif
6183 }
6184
6185 void PlayMenuSound()
6186 {
6187   int sound = menu.sound[game_status];
6188
6189   if (sound == SND_UNDEFINED)
6190     return;
6191
6192   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
6193       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
6194     return;
6195
6196   if (IS_LOOP_SOUND(sound))
6197     PlaySoundLoop(sound);
6198   else
6199     PlaySound(sound);
6200 }
6201
6202 void PlayMenuSoundStereo(int sound, int stereo_position)
6203 {
6204   if (sound == SND_UNDEFINED)
6205     return;
6206
6207   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
6208       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
6209     return;
6210
6211   if (IS_LOOP_SOUND(sound))
6212     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
6213   else
6214     PlaySoundStereo(sound, stereo_position);
6215 }
6216
6217 void PlayMenuSoundIfLoop()
6218 {
6219   int sound = menu.sound[game_status];
6220
6221   if (sound == SND_UNDEFINED)
6222     return;
6223
6224   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
6225       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
6226     return;
6227
6228   if (IS_LOOP_SOUND(sound))
6229     PlaySoundLoop(sound);
6230 }
6231
6232 void PlayMenuMusic()
6233 {
6234   int music = menu.music[game_status];
6235
6236   if (music == MUS_UNDEFINED)
6237     return;
6238
6239   if (!setup.sound_music)
6240     return;
6241
6242   PlayMusic(music);
6243 }
6244
6245 void PlaySoundActivating()
6246 {
6247 #if 0
6248   PlaySound(SND_MENU_ITEM_ACTIVATING);
6249 #endif
6250 }
6251
6252 void PlaySoundSelecting()
6253 {
6254 #if 0
6255   PlaySound(SND_MENU_ITEM_SELECTING);
6256 #endif
6257 }
6258
6259 void ToggleFullscreenIfNeeded()
6260 {
6261   boolean change_fullscreen = (setup.fullscreen !=
6262                                video.fullscreen_enabled);
6263   boolean change_fullscreen_mode = (video.fullscreen_enabled &&
6264                                     !strEqual(setup.fullscreen_mode,
6265                                               video.fullscreen_mode_current));
6266
6267   if (!video.fullscreen_available)
6268     return;
6269
6270 #if 1
6271   if (change_fullscreen || change_fullscreen_mode)
6272 #else
6273   if (setup.fullscreen != video.fullscreen_enabled ||
6274       setup.fullscreen_mode != video.fullscreen_mode_current)
6275 #endif
6276   {
6277     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
6278
6279     /* save backbuffer content which gets lost when toggling fullscreen mode */
6280     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
6281
6282 #if 1
6283     if (change_fullscreen_mode)
6284 #else
6285     if (setup.fullscreen && video.fullscreen_enabled)
6286 #endif
6287     {
6288       /* keep fullscreen, but change fullscreen mode (screen resolution) */
6289 #if 1
6290       /* (this is now set in sdl.c) */
6291 #else
6292       video.fullscreen_mode_current = setup.fullscreen_mode;
6293 #endif
6294       video.fullscreen_enabled = FALSE;         /* force new fullscreen mode */
6295     }
6296
6297     /* toggle fullscreen */
6298     ChangeVideoModeIfNeeded(setup.fullscreen);
6299
6300     setup.fullscreen = video.fullscreen_enabled;
6301
6302     /* restore backbuffer content from temporary backbuffer backup bitmap */
6303     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
6304
6305     FreeBitmap(tmp_backbuffer);
6306
6307 #if 1
6308     /* update visible window/screen */
6309     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
6310 #else
6311     redraw_mask = REDRAW_ALL;
6312 #endif
6313   }
6314 }