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