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