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