moved video frame synchronization to single location (screen update)
[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_GFX_DOOR_1_PART_1,
70     &door_1.part_1
71   },
72   {
73     DOOR_1,
74     IMG_GFX_DOOR_1_PART_2,
75     &door_1.part_2
76   },
77   {
78     DOOR_1,
79     IMG_GFX_DOOR_1_PART_3,
80     &door_1.part_3
81   },
82   {
83     DOOR_1,
84     IMG_GFX_DOOR_1_PART_4,
85     &door_1.part_4
86   },
87   {
88     DOOR_1,
89     IMG_GFX_DOOR_1_PART_5,
90     &door_1.part_5
91   },
92   {
93     DOOR_1,
94     IMG_GFX_DOOR_1_PART_6,
95     &door_1.part_6
96   },
97   {
98     DOOR_1,
99     IMG_GFX_DOOR_1_PART_7,
100     &door_1.part_7
101   },
102   {
103     DOOR_1,
104     IMG_GFX_DOOR_1_PART_8,
105     &door_1.part_8
106   },
107
108   {
109     DOOR_2,
110     IMG_GFX_DOOR_2_PART_1,
111     &door_2.part_1
112   },
113   {
114     DOOR_2,
115     IMG_GFX_DOOR_2_PART_2,
116     &door_2.part_2
117   },
118   {
119     DOOR_2,
120     IMG_GFX_DOOR_2_PART_3,
121     &door_2.part_3
122   },
123   {
124     DOOR_2,
125     IMG_GFX_DOOR_2_PART_4,
126     &door_2.part_4
127   },
128   {
129     DOOR_2,
130     IMG_GFX_DOOR_2_PART_5,
131     &door_2.part_5
132   },
133   {
134     DOOR_2,
135     IMG_GFX_DOOR_2_PART_6,
136     &door_2.part_6
137   },
138   {
139     DOOR_2,
140     IMG_GFX_DOOR_2_PART_7,
141     &door_2.part_7
142   },
143   {
144     DOOR_2,
145     IMG_GFX_DOOR_2_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 char *print_if_not_empty(int element)
178 {
179   static char *s = NULL;
180   char *token_name = element_info[element].token_name;
181
182   if (s != NULL)
183     free(s);
184
185   s = checked_malloc(strlen(token_name) + 10 + 1);
186
187   if (element != EL_EMPTY)
188     sprintf(s, "%d\t['%s']", element, token_name);
189   else
190     sprintf(s, "%d", element);
191
192   return s;
193 }
194
195 void DumpTile(int x, int y)
196 {
197   int sx = SCREENX(x);
198   int sy = SCREENY(y);
199
200   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
201   {
202     x--;
203     y--;
204   }
205
206   printf_line("-", 79);
207   printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
208   printf_line("-", 79);
209
210   if (!IN_LEV_FIELD(x, y))
211   {
212     printf("(not in level field)\n");
213     printf("\n");
214
215     return;
216   }
217
218   printf("  Feld:        %d\t['%s']\n", Feld[x][y],
219          element_info[Feld[x][y]].token_name);
220   printf("  Back:        %s\n", print_if_not_empty(Back[x][y]));
221   printf("  Store:       %s\n", print_if_not_empty(Store[x][y]));
222   printf("  Store2:      %s\n", print_if_not_empty(Store2[x][y]));
223   printf("  StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
224   printf("  MovPos:      %d\n", MovPos[x][y]);
225   printf("  MovDir:      %d\n", MovDir[x][y]);
226   printf("  MovDelay:    %d\n", MovDelay[x][y]);
227   printf("  ChangeDelay: %d\n", ChangeDelay[x][y]);
228   printf("  CustomValue: %d\n", CustomValue[x][y]);
229   printf("  GfxElement:  %d\n", GfxElement[x][y]);
230   printf("  GfxAction:   %d\n", GfxAction[x][y]);
231   printf("  GfxFrame:    %d [%d]\n", GfxFrame[x][y], FrameCounter);
232   printf("\n");
233 }
234
235 void SetDrawtoField(int mode)
236 {
237   if (mode == DRAW_FIELDBUFFER)
238   {
239     FX = 2 * TILEX_VAR;
240     FY = 2 * TILEY_VAR;
241     BX1 = -2;
242     BY1 = -2;
243     BX2 = SCR_FIELDX + 1;
244     BY2 = SCR_FIELDY + 1;
245
246     drawto_field = fieldbuffer;
247   }
248   else  /* DRAW_BACKBUFFER */
249   {
250     FX = SX;
251     FY = SY;
252     BX1 = 0;
253     BY1 = 0;
254     BX2 = SCR_FIELDX - 1;
255     BY2 = SCR_FIELDY - 1;
256
257     drawto_field = backbuffer;
258   }
259 }
260
261 static void RedrawPlayfield_RND()
262 {
263   if (game.envelope_active)
264     return;
265
266   DrawLevel(REDRAW_ALL);
267   DrawAllPlayers();
268 }
269
270 void RedrawPlayfield()
271 {
272   if (game_status != GAME_MODE_PLAYING)
273     return;
274
275   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
276     RedrawPlayfield_EM(TRUE);
277   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
278     RedrawPlayfield_SP(TRUE);
279   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
280     RedrawPlayfield_RND();
281
282   BlitScreenToBitmap(backbuffer);
283
284   BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
285              gfx.sx, gfx.sy);
286 }
287
288 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
289                                      int draw_target)
290 {
291   Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
292   Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
293
294   if (x == -1 && y == -1)
295     return;
296
297   if (draw_target == DRAW_BORDER_TO_SCREEN)
298     BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
299   else
300     BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
301 }
302
303 static void DrawMaskedBorderExt_FIELD(int draw_target)
304 {
305   if (global.border_status >= GAME_MODE_MAIN &&
306       global.border_status <= GAME_MODE_PLAYING &&
307       border.draw_masked[global.border_status])
308     DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
309                              draw_target);
310 }
311
312 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
313 {
314   // when drawing to backbuffer, never draw border over open doors
315   if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
316       (GetDoorState() & DOOR_OPEN_1))
317     return;
318
319   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
320       (global.border_status != GAME_MODE_EDITOR ||
321        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
322     DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
323 }
324
325 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
326 {
327   // when drawing to backbuffer, never draw border over open doors
328   if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
329       (GetDoorState() & DOOR_OPEN_2))
330     return;
331
332   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
333       global.border_status != GAME_MODE_EDITOR)
334     DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
335 }
336
337 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
338 {
339   /* currently not available */
340 }
341
342 static void DrawMaskedBorderExt_ALL(int draw_target)
343 {
344   DrawMaskedBorderExt_FIELD(draw_target);
345   DrawMaskedBorderExt_DOOR_1(draw_target);
346   DrawMaskedBorderExt_DOOR_2(draw_target);
347   DrawMaskedBorderExt_DOOR_3(draw_target);
348 }
349
350 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
351 {
352   /* never draw masked screen borders on borderless screens */
353   if (global.border_status == GAME_MODE_LOADING ||
354       global.border_status == GAME_MODE_TITLE)
355     return;
356
357   if (redraw_mask & REDRAW_ALL)
358     DrawMaskedBorderExt_ALL(draw_target);
359   else
360   {
361     if (redraw_mask & REDRAW_FIELD)
362       DrawMaskedBorderExt_FIELD(draw_target);
363     if (redraw_mask & REDRAW_DOOR_1)
364       DrawMaskedBorderExt_DOOR_1(draw_target);
365     if (redraw_mask & REDRAW_DOOR_2)
366       DrawMaskedBorderExt_DOOR_2(draw_target);
367     if (redraw_mask & REDRAW_DOOR_3)
368       DrawMaskedBorderExt_DOOR_3(draw_target);
369   }
370 }
371
372 void DrawMaskedBorder_FIELD()
373 {
374   DrawMaskedBorderExt_FIELD(DRAW_BORDER_TO_BACKBUFFER);
375 }
376
377 void DrawMaskedBorder(int redraw_mask)
378 {
379   DrawMaskedBorderExt(redraw_mask, DRAW_BORDER_TO_BACKBUFFER);
380 }
381
382 void DrawMaskedBorderToTarget(int draw_target)
383 {
384   if (draw_target == DRAW_BORDER_TO_BACKBUFFER ||
385       draw_target == DRAW_BORDER_TO_SCREEN)
386   {
387     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
388   }
389   else
390   {
391     int last_border_status = global.border_status;
392
393     if (draw_target == DRAW_BORDER_TO_FADE_SOURCE)
394     {
395       global.border_status = gfx.fade_border_source_status;
396       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
397     }
398     else if (draw_target == DRAW_BORDER_TO_FADE_TARGET)
399     {
400       global.border_status = gfx.fade_border_target_status;
401       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
402     }
403
404     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
405
406     global.border_status = last_border_status;
407     gfx.masked_border_bitmap_ptr = backbuffer;
408   }
409 }
410
411 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
412 {
413   int fx = FX, fy = FY;
414   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
415   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
416
417   int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
418   int dy = (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
419   int dx_var = dx * TILESIZE_VAR / TILESIZE;
420   int dy_var = dy * TILESIZE_VAR / TILESIZE;
421   int ffx, ffy;
422
423   ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
424   ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
425
426   if (EVEN(SCR_FIELDX))
427   {
428     if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
429       fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
430     else
431       fx += (dx_var > 0 ? TILEX_VAR : 0);
432   }
433   else
434   {
435     fx += dx_var;
436   }
437
438   if (EVEN(SCR_FIELDY))
439   {
440     if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
441       fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
442     else
443       fy += (dy_var > 0 ? TILEY_VAR : 0);
444   }
445   else
446   {
447     fy += dy_var;
448   }
449
450   if (full_lev_fieldx <= SCR_FIELDX)
451   {
452     if (EVEN(SCR_FIELDX))
453       fx = 2 * TILEX_VAR - (ODD(lev_fieldx)  ? TILEX_VAR / 2 : 0);
454     else
455       fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
456   }
457
458   if (full_lev_fieldy <= SCR_FIELDY)
459   {
460     if (EVEN(SCR_FIELDY))
461       fy = 2 * TILEY_VAR - (ODD(lev_fieldy)  ? TILEY_VAR / 2 : 0);
462     else
463       fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
464   }
465
466   BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
467 }
468
469 void BlitScreenToBitmap(Bitmap *target_bitmap)
470 {
471   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
472     BlitScreenToBitmap_EM(target_bitmap);
473   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
474     BlitScreenToBitmap_SP(target_bitmap);
475   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
476     BlitScreenToBitmap_RND(target_bitmap);
477
478   redraw_mask |= REDRAW_FIELD;
479 }
480
481 void DrawFramesPerSecond()
482 {
483   char text[100];
484   int font_nr = FONT_TEXT_2;
485   int font_width = getFontWidth(font_nr);
486
487   sprintf(text, "%04.1f fps", global.frames_per_second);
488
489   DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
490               font_nr, BLIT_OPAQUE);
491 }
492
493 #if DEBUG_FRAME_TIME
494 static void PrintFrameTimeDebugging()
495 {
496   static unsigned int last_counter = 0;
497   unsigned int counter = Counter();
498   int diff_1 = counter - last_counter;
499   int diff_2 = diff_1 - GAME_FRAME_DELAY;
500   int diff_2_max = 20;
501   int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
502   char diff_bar[2 * diff_2_max + 5];
503   int pos = 0;
504   int i;
505
506   diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
507
508   for (i = 0; i < diff_2_max; i++)
509     diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
510                        i >= diff_2_max - diff_2_cut ? '-' : ' ');
511
512   diff_bar[pos++] = '|';
513
514   for (i = 0; i < diff_2_max; i++)
515     diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
516
517   diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
518
519   diff_bar[pos++] = '\0';
520
521   Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
522         counter,
523         diff_1,
524         (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
525         diff_bar);
526
527   last_counter = counter;
528 }
529 #endif
530
531 void BackToFront()
532 {
533   static int last_redraw_mask = REDRAW_NONE;
534
535   // force screen redraw in every frame to continue drawing global animations
536   // (but always use the last redraw mask to prevent unwanted side effects)
537   if (redraw_mask == REDRAW_NONE)
538     redraw_mask = last_redraw_mask;
539
540   last_redraw_mask = redraw_mask;
541
542 #if 1
543   // masked border now drawn immediately when blitting backbuffer to window
544 #else
545   // draw masked border to all viewports, if defined
546   DrawMaskedBorder(redraw_mask);
547 #endif
548
549   // draw frames per second (only if debug mode is enabled)
550   if (redraw_mask & REDRAW_FPS)
551     DrawFramesPerSecond();
552
553   // redraw complete window if both playfield and (some) doors need redraw
554   if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
555     redraw_mask = REDRAW_ALL;
556
557   /* although redrawing the whole window would be fine for normal gameplay,
558      being able to only redraw the playfield is required for deactivating
559      certain drawing areas (mainly playfield) to work, which is needed for
560      warp-forward to be fast enough (by skipping redraw of most frames) */
561
562   if (redraw_mask & REDRAW_ALL)
563   {
564     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
565   }
566   else if (redraw_mask & REDRAW_FIELD)
567   {
568     BlitBitmap(backbuffer, window,
569                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
570   }
571   else if (redraw_mask & REDRAW_DOORS)
572   {
573     if (redraw_mask & REDRAW_DOOR_1)
574       BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
575
576     if (redraw_mask & REDRAW_DOOR_2)
577       BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
578
579     if (redraw_mask & REDRAW_DOOR_3)
580       BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
581   }
582
583   redraw_mask = REDRAW_NONE;
584
585 #if DEBUG_FRAME_TIME
586   PrintFrameTimeDebugging();
587 #endif
588 }
589
590 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
591 {
592   unsigned int frame_delay_value_old = GetVideoFrameDelay();
593
594   SetVideoFrameDelay(frame_delay_value);
595
596   BackToFront();
597
598   SetVideoFrameDelay(frame_delay_value_old);
599 }
600
601 static void FadeCrossSaveBackbuffer()
602 {
603   BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
604 }
605
606 static void FadeCrossRestoreBackbuffer()
607 {
608   int redraw_mask_last = redraw_mask;
609
610   BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
611
612   // do not change redraw mask when restoring backbuffer after cross-fading
613   redraw_mask = redraw_mask_last;
614 }
615
616 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
617 {
618   static int fade_type_skip = FADE_TYPE_NONE;
619   void (*draw_border_function)(void) = NULL;
620   Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
621   int x, y, width, height;
622   int fade_delay, post_delay;
623
624   if (fade_type == FADE_TYPE_FADE_OUT)
625   {
626     if (fade_type_skip != FADE_TYPE_NONE)
627     {
628       /* skip all fade operations until specified fade operation */
629       if (fade_type & fade_type_skip)
630         fade_type_skip = FADE_TYPE_NONE;
631
632       return;
633     }
634
635 #if 1
636     FadeCrossSaveBackbuffer();
637 #endif
638
639     if (fading.fade_mode & FADE_TYPE_TRANSFORM)
640     {
641 #if 0
642       FadeCrossSaveBackbuffer();
643 #endif
644
645       return;
646     }
647   }
648
649   redraw_mask |= fade_mask;
650
651   if (fade_type == FADE_TYPE_SKIP)
652   {
653     fade_type_skip = fade_mode;
654
655     return;
656   }
657
658   fade_delay = fading.fade_delay;
659   post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
660
661   if (fade_type_skip != FADE_TYPE_NONE)
662   {
663     /* skip all fade operations until specified fade operation */
664     if (fade_type & fade_type_skip)
665       fade_type_skip = FADE_TYPE_NONE;
666
667     fade_delay = 0;
668   }
669
670   if (global.autoplay_leveldir)
671   {
672     return;
673   }
674
675   if (fade_mask == REDRAW_FIELD)
676   {
677     x = FADE_SX;
678     y = FADE_SY;
679     width  = FADE_SXSIZE;
680     height = FADE_SYSIZE;
681
682     if (border.draw_masked_when_fading)
683       draw_border_function = DrawMaskedBorder_FIELD;    /* update when fading */
684     else
685       DrawMaskedBorder_FIELD();                         /* draw once */
686   }
687   else          /* REDRAW_ALL */
688   {
689     x = 0;
690     y = 0;
691     width  = WIN_XSIZE;
692     height = WIN_YSIZE;
693   }
694
695   if (!setup.fade_screens ||
696       fade_delay == 0 ||
697       fading.fade_mode == FADE_MODE_NONE)
698   {
699     if (fade_mode == FADE_MODE_FADE_OUT)
700       return;
701
702     BlitBitmap(backbuffer, window, x, y, width, height, x, y);
703
704     redraw_mask &= ~fade_mask;
705
706     return;
707   }
708
709   FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
710                 draw_border_function);
711
712   if (fade_type == FADE_TYPE_FADE_OUT)
713     FadeCrossRestoreBackbuffer();
714
715   redraw_mask &= ~fade_mask;
716 }
717
718 static void SetScreenStates_BeforeFadingIn()
719 {
720 }
721
722 static void SetScreenStates_AfterFadingIn()
723 {
724   // store new source screen (to use correct masked border for fading)
725   gfx.fade_border_source_status = global.border_status;
726
727   global.anim_status = global.anim_status_next;
728
729   // force update of global animation status in case of rapid screen changes
730   redraw_mask = REDRAW_ALL;
731   BackToFront();
732 }
733
734 static void SetScreenStates_BeforeFadingOut()
735 {
736   // store new target screen (to use correct masked border for fading)
737   gfx.fade_border_target_status = game_status;
738
739   global.anim_status = GAME_MODE_PSEUDO_FADING;
740 }
741
742 static void SetScreenStates_AfterFadingOut()
743 {
744   global.border_status = game_status;
745 }
746
747 void FadeIn(int fade_mask)
748 {
749   SetScreenStates_BeforeFadingIn();
750
751 #if 1
752   DrawMaskedBorder(REDRAW_ALL);
753 #endif
754
755   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
756     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
757   else
758     FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
759
760   FADE_SX = REAL_SX;
761   FADE_SY = REAL_SY;
762   FADE_SXSIZE = FULL_SXSIZE;
763   FADE_SYSIZE = FULL_SYSIZE;
764
765   SetScreenStates_AfterFadingIn();
766 }
767
768 void FadeOut(int fade_mask)
769 {
770   SetScreenStates_BeforeFadingOut();
771
772 #if 0
773   DrawMaskedBorder(REDRAW_ALL);
774 #endif
775
776   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
777     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
778   else
779     FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
780
781   SetScreenStates_AfterFadingOut();
782 }
783
784 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
785 {
786   static struct TitleFadingInfo fading_leave_stored;
787
788   if (set)
789     fading_leave_stored = fading_leave;
790   else
791     fading = fading_leave_stored;
792 }
793
794 void FadeSetEnterMenu()
795 {
796   fading = menu.enter_menu;
797
798   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
799 }
800
801 void FadeSetLeaveMenu()
802 {
803   fading = menu.leave_menu;
804
805   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
806 }
807
808 void FadeSetEnterScreen()
809 {
810   fading = menu.enter_screen[game_status];
811
812   FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);       /* store */
813 }
814
815 void FadeSetNextScreen()
816 {
817   fading = menu.next_screen[game_status];
818
819   // (do not overwrite fade mode set by FadeSetEnterScreen)
820   // FadeSetLeaveNext(fading, TRUE);    /* (keep same fade mode) */
821 }
822
823 void FadeSetLeaveScreen()
824 {
825   FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);      /* recall */
826 }
827
828 void FadeSetFromType(int type)
829 {
830   if (type & TYPE_ENTER_SCREEN)
831     FadeSetEnterScreen();
832   else if (type & TYPE_ENTER)
833     FadeSetEnterMenu();
834   else if (type & TYPE_LEAVE)
835     FadeSetLeaveMenu();
836 }
837
838 void FadeSetDisabled()
839 {
840   static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
841
842   fading = fading_none;
843 }
844
845 void FadeSkipNextFadeIn()
846 {
847   FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
848 }
849
850 void FadeSkipNextFadeOut()
851 {
852   FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
853 }
854
855 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
856 {
857   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
858
859   return (graphic == IMG_UNDEFINED ? NULL :
860           graphic_info[graphic].bitmap != NULL || redefined ?
861           graphic_info[graphic].bitmap :
862           graphic_info[default_graphic].bitmap);
863 }
864
865 Bitmap *getBackgroundBitmap(int graphic)
866 {
867   return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
868 }
869
870 Bitmap *getGlobalBorderBitmap(int graphic)
871 {
872   return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
873 }
874
875 Bitmap *getGlobalBorderBitmapFromStatus(int status)
876 {
877   int graphic =
878     (status == GAME_MODE_MAIN ||
879      status == GAME_MODE_PSEUDO_TYPENAME        ? IMG_GLOBAL_BORDER_MAIN :
880      status == GAME_MODE_SCORES                 ? IMG_GLOBAL_BORDER_SCORES :
881      status == GAME_MODE_EDITOR                 ? IMG_GLOBAL_BORDER_EDITOR :
882      status == GAME_MODE_PLAYING                ? IMG_GLOBAL_BORDER_PLAYING :
883      IMG_GLOBAL_BORDER);
884
885   return getGlobalBorderBitmap(graphic);
886 }
887
888 void SetWindowBackgroundImageIfDefined(int graphic)
889 {
890   if (graphic_info[graphic].bitmap)
891     SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
892 }
893
894 void SetMainBackgroundImageIfDefined(int graphic)
895 {
896   if (graphic_info[graphic].bitmap)
897     SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
898 }
899
900 void SetDoorBackgroundImageIfDefined(int graphic)
901 {
902   if (graphic_info[graphic].bitmap)
903     SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
904 }
905
906 void SetWindowBackgroundImage(int graphic)
907 {
908   SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
909 }
910
911 void SetMainBackgroundImage(int graphic)
912 {
913   SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
914 }
915
916 void SetDoorBackgroundImage(int graphic)
917 {
918   SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
919 }
920
921 void SetPanelBackground()
922 {
923   struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
924
925   BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
926                   gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
927
928   SetDoorBackgroundBitmap(bitmap_db_panel);
929 }
930
931 void DrawBackground(int x, int y, int width, int height)
932 {
933   /* "drawto" might still point to playfield buffer here (hall of fame) */
934   ClearRectangleOnBackground(backbuffer, x, y, width, height);
935
936   if (IN_GFX_FIELD_FULL(x, y))
937     redraw_mask |= REDRAW_FIELD;
938   else if (IN_GFX_DOOR_1(x, y))
939     redraw_mask |= REDRAW_DOOR_1;
940   else if (IN_GFX_DOOR_2(x, y))
941     redraw_mask |= REDRAW_DOOR_2;
942   else if (IN_GFX_DOOR_3(x, y))
943     redraw_mask |= REDRAW_DOOR_3;
944 }
945
946 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
947 {
948   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
949
950   if (font->bitmap == NULL)
951     return;
952
953   DrawBackground(x, y, width, height);
954 }
955
956 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
957 {
958   struct GraphicInfo *g = &graphic_info[graphic];
959
960   if (g->bitmap == NULL)
961     return;
962
963   DrawBackground(x, y, width, height);
964 }
965
966 static int game_status_last = -1;
967 static Bitmap *global_border_bitmap_last = NULL;
968 static Bitmap *global_border_bitmap = NULL;
969 static int real_sx_last = -1, real_sy_last = -1;
970 static int full_sxsize_last = -1, full_sysize_last = -1;
971 static int dx_last = -1, dy_last = -1;
972 static int dxsize_last = -1, dysize_last = -1;
973 static int vx_last = -1, vy_last = -1;
974 static int vxsize_last = -1, vysize_last = -1;
975
976 boolean CheckIfGlobalBorderHasChanged()
977 {
978   // if game status has not changed, global border has not changed either
979   if (game_status == game_status_last)
980     return FALSE;
981
982   // determine and store new global border bitmap for current game status
983   global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
984
985   return (global_border_bitmap_last != global_border_bitmap);
986 }
987
988 boolean CheckIfGlobalBorderRedrawIsNeeded()
989 {
990   // if game status has not changed, nothing has to be redrawn
991   if (game_status == game_status_last)
992     return FALSE;
993
994   // redraw if last screen was title screen
995   if (game_status_last == GAME_MODE_TITLE)
996     return TRUE;
997
998   // redraw if global screen border has changed
999   if (CheckIfGlobalBorderHasChanged())
1000     return TRUE;
1001
1002   // redraw if position or size of playfield area has changed
1003   if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1004       full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1005     return TRUE;
1006
1007   // redraw if position or size of door area has changed
1008   if (dx_last != DX || dy_last != DY ||
1009       dxsize_last != DXSIZE || dysize_last != DYSIZE)
1010     return TRUE;
1011
1012   // redraw if position or size of tape area has changed
1013   if (vx_last != VX || vy_last != VY ||
1014       vxsize_last != VXSIZE || vysize_last != VYSIZE)
1015     return TRUE;
1016
1017   return FALSE;
1018 }
1019
1020 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1021 {
1022   if (bitmap)
1023     BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1024   else
1025     ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1026 }
1027
1028 void RedrawGlobalBorder()
1029 {
1030   Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1031
1032   RedrawGlobalBorderFromBitmap(bitmap);
1033
1034   redraw_mask = REDRAW_ALL;
1035 }
1036
1037 static void RedrawGlobalBorderIfNeeded()
1038 {
1039   if (game_status == game_status_last)
1040     return;
1041
1042   // copy current draw buffer to later copy back areas that have not changed
1043   if (game_status_last != GAME_MODE_TITLE)
1044     BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1045
1046   if (CheckIfGlobalBorderRedrawIsNeeded())
1047   {
1048     // redraw global screen border (or clear, if defined to be empty)
1049     RedrawGlobalBorderFromBitmap(global_border_bitmap);
1050
1051     // copy previous playfield and door areas, if they are defined on both
1052     // previous and current screen and if they still have the same size
1053
1054     if (real_sx_last != -1 && real_sy_last != -1 &&
1055         REAL_SX != -1 && REAL_SY != -1 &&
1056         full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1057       BlitBitmap(bitmap_db_store, backbuffer,
1058                  real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1059                  REAL_SX, REAL_SY);
1060
1061     if (dx_last != -1 && dy_last != -1 &&
1062         DX != -1 && DY != -1 &&
1063         dxsize_last == DXSIZE && dysize_last == DYSIZE)
1064       BlitBitmap(bitmap_db_store, backbuffer,
1065                  dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1066
1067     if (vx_last != -1 && vy_last != -1 &&
1068         VX != -1 && VY != -1 &&
1069         vxsize_last == VXSIZE && vysize_last == VYSIZE)
1070       BlitBitmap(bitmap_db_store, backbuffer,
1071                  vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1072
1073     redraw_mask = REDRAW_ALL;
1074   }
1075
1076   game_status_last = game_status;
1077
1078   global_border_bitmap_last = global_border_bitmap;
1079
1080   real_sx_last = REAL_SX;
1081   real_sy_last = REAL_SY;
1082   full_sxsize_last = FULL_SXSIZE;
1083   full_sysize_last = FULL_SYSIZE;
1084   dx_last = DX;
1085   dy_last = DY;
1086   dxsize_last = DXSIZE;
1087   dysize_last = DYSIZE;
1088   vx_last = VX;
1089   vy_last = VY;
1090   vxsize_last = VXSIZE;
1091   vysize_last = VYSIZE;
1092 }
1093
1094 void ClearField()
1095 {
1096   RedrawGlobalBorderIfNeeded();
1097
1098   /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1099   /* (when entering hall of fame after playing) */
1100   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1101
1102   /* !!! maybe this should be done before clearing the background !!! */
1103   if (game_status == GAME_MODE_PLAYING)
1104   {
1105     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1106     SetDrawtoField(DRAW_FIELDBUFFER);
1107   }
1108   else
1109   {
1110     SetDrawtoField(DRAW_BACKBUFFER);
1111   }
1112 }
1113
1114 void MarkTileDirty(int x, int y)
1115 {
1116   redraw_mask |= REDRAW_FIELD;
1117 }
1118
1119 void SetBorderElement()
1120 {
1121   int x, y;
1122
1123   BorderElement = EL_EMPTY;
1124
1125   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1126   {
1127     for (x = 0; x < lev_fieldx; x++)
1128     {
1129       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1130         BorderElement = EL_STEELWALL;
1131
1132       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1133         x = lev_fieldx - 2;
1134     }
1135   }
1136 }
1137
1138 void FloodFillLevel(int from_x, int from_y, int fill_element,
1139                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1140                     int max_fieldx, int max_fieldy)
1141 {
1142   int i,x,y;
1143   int old_element;
1144   static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1145   static int safety = 0;
1146
1147   /* check if starting field still has the desired content */
1148   if (field[from_x][from_y] == fill_element)
1149     return;
1150
1151   safety++;
1152
1153   if (safety > max_fieldx * max_fieldy)
1154     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1155
1156   old_element = field[from_x][from_y];
1157   field[from_x][from_y] = fill_element;
1158
1159   for (i = 0; i < 4; i++)
1160   {
1161     x = from_x + check[i][0];
1162     y = from_y + check[i][1];
1163
1164     if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1165       FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1166   }
1167
1168   safety--;
1169 }
1170
1171 void SetRandomAnimationValue(int x, int y)
1172 {
1173   gfx.anim_random_frame = GfxRandom[x][y];
1174 }
1175
1176 int getGraphicAnimationFrame(int graphic, int sync_frame)
1177 {
1178   /* animation synchronized with global frame counter, not move position */
1179   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1180     sync_frame = FrameCounter;
1181
1182   return getAnimationFrame(graphic_info[graphic].anim_frames,
1183                            graphic_info[graphic].anim_delay,
1184                            graphic_info[graphic].anim_mode,
1185                            graphic_info[graphic].anim_start_frame,
1186                            sync_frame);
1187 }
1188
1189 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1190                               Bitmap **bitmap, int *x, int *y,
1191                               boolean get_backside)
1192 {
1193   struct GraphicInfo *g = &graphic_info[graphic];
1194   Bitmap *src_bitmap = g->bitmap;
1195   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1196   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1197   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1198
1199   // if no in-game graphics defined, always use standard graphic size
1200   if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1201     tilesize = TILESIZE;
1202
1203   if (tilesize == gfx.standard_tile_size)
1204     src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1205   else if (tilesize == game.tile_size)
1206     src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1207   else
1208     src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1209
1210   if (g->offset_y == 0)         /* frames are ordered horizontally */
1211   {
1212     int max_width = g->anim_frames_per_line * g->width;
1213     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1214
1215     src_x = pos % max_width;
1216     src_y = src_y % g->height + pos / max_width * g->height;
1217   }
1218   else if (g->offset_x == 0)    /* frames are ordered vertically */
1219   {
1220     int max_height = g->anim_frames_per_line * g->height;
1221     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1222
1223     src_x = src_x % g->width + pos / max_height * g->width;
1224     src_y = pos % max_height;
1225   }
1226   else                          /* frames are ordered diagonally */
1227   {
1228     src_x = src_x + frame * g->offset_x;
1229     src_y = src_y + frame * g->offset_y;
1230   }
1231
1232   *bitmap = src_bitmap;
1233   *x = src_x * tilesize / g->tile_size;
1234   *y = src_y * tilesize / g->tile_size;
1235 }
1236
1237 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1238                               int *x, int *y, boolean get_backside)
1239 {
1240   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1241                            get_backside);
1242 }
1243
1244 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1245                            Bitmap **bitmap, int *x, int *y)
1246 {
1247   getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1248 }
1249
1250 void getFixedGraphicSource(int graphic, int frame,
1251                            Bitmap **bitmap, int *x, int *y)
1252 {
1253   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1254 }
1255
1256 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1257 {
1258   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1259 }
1260
1261 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1262                                        int *x, int *y, boolean get_backside)
1263 {
1264   struct GraphicInfo *g = &graphic_info[graphic];
1265   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1266   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1267
1268   if (TILESIZE_VAR != TILESIZE)
1269     return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1270                                     get_backside);
1271
1272   *bitmap = g->bitmap;
1273
1274   if (g->offset_y == 0)         /* frames are ordered horizontally */
1275   {
1276     int max_width = g->anim_frames_per_line * g->width;
1277     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1278
1279     *x = pos % max_width;
1280     *y = src_y % g->height + pos / max_width * g->height;
1281   }
1282   else if (g->offset_x == 0)    /* frames are ordered vertically */
1283   {
1284     int max_height = g->anim_frames_per_line * g->height;
1285     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1286
1287     *x = src_x % g->width + pos / max_height * g->width;
1288     *y = pos % max_height;
1289   }
1290   else                          /* frames are ordered diagonally */
1291   {
1292     *x = src_x + frame * g->offset_x;
1293     *y = src_y + frame * g->offset_y;
1294   }
1295
1296   *x = *x * TILESIZE_VAR / g->tile_size;
1297   *y = *y * TILESIZE_VAR / g->tile_size;
1298 }
1299
1300 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1301 {
1302   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1303 }
1304
1305 void DrawGraphic(int x, int y, int graphic, int frame)
1306 {
1307 #if DEBUG
1308   if (!IN_SCR_FIELD(x, y))
1309   {
1310     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1311     printf("DrawGraphic(): This should never happen!\n");
1312     return;
1313   }
1314 #endif
1315
1316   DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1317                  frame);
1318
1319   MarkTileDirty(x, y);
1320 }
1321
1322 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1323 {
1324 #if DEBUG
1325   if (!IN_SCR_FIELD(x, y))
1326   {
1327     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1328     printf("DrawGraphic(): This should never happen!\n");
1329     return;
1330   }
1331 #endif
1332
1333   DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1334                       frame);
1335   MarkTileDirty(x, y);
1336 }
1337
1338 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1339                     int frame)
1340 {
1341   Bitmap *src_bitmap;
1342   int src_x, src_y;
1343
1344   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1345
1346   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1347 }
1348
1349 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1350                          int frame)
1351 {
1352   Bitmap *src_bitmap;
1353   int src_x, src_y;
1354
1355   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1356   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1357 }
1358
1359 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1360 {
1361 #if DEBUG
1362   if (!IN_SCR_FIELD(x, y))
1363   {
1364     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1365     printf("DrawGraphicThruMask(): This should never happen!\n");
1366     return;
1367   }
1368 #endif
1369
1370   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1371                          graphic, frame);
1372
1373   MarkTileDirty(x, y);
1374 }
1375
1376 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1377 {
1378 #if DEBUG
1379   if (!IN_SCR_FIELD(x, y))
1380   {
1381     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1382     printf("DrawGraphicThruMask(): This should never happen!\n");
1383     return;
1384   }
1385 #endif
1386
1387   DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1388                               graphic, frame);
1389   MarkTileDirty(x, y);
1390 }
1391
1392 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1393                             int frame)
1394 {
1395   Bitmap *src_bitmap;
1396   int src_x, src_y;
1397
1398   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1399
1400   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1401                    dst_x, dst_y);
1402 }
1403
1404 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1405                                  int graphic, int frame)
1406 {
1407   Bitmap *src_bitmap;
1408   int src_x, src_y;
1409
1410   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1411
1412   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1413                    dst_x, dst_y);
1414 }
1415
1416 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1417 {
1418   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1419                       frame, tilesize);
1420   MarkTileDirty(x / tilesize, y / tilesize);
1421 }
1422
1423 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1424                          int tilesize)
1425 {
1426   Bitmap *src_bitmap;
1427   int src_x, src_y;
1428
1429   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1430   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1431 }
1432
1433 void DrawMiniGraphic(int x, int y, int graphic)
1434 {
1435   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1436   MarkTileDirty(x / 2, y / 2);
1437 }
1438
1439 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1440 {
1441   Bitmap *src_bitmap;
1442   int src_x, src_y;
1443
1444   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1445   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1446 }
1447
1448 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1449                                             int graphic, int frame,
1450                                             int cut_mode, int mask_mode)
1451 {
1452   Bitmap *src_bitmap;
1453   int src_x, src_y;
1454   int dst_x, dst_y;
1455   int width = TILEX, height = TILEY;
1456   int cx = 0, cy = 0;
1457
1458   if (dx || dy)                 /* shifted graphic */
1459   {
1460     if (x < BX1)                /* object enters playfield from the left */
1461     {
1462       x = BX1;
1463       width = dx;
1464       cx = TILEX - dx;
1465       dx = 0;
1466     }
1467     else if (x > BX2)           /* object enters playfield from the right */
1468     {
1469       x = BX2;
1470       width = -dx;
1471       dx = TILEX + dx;
1472     }
1473     else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1474     {
1475       width += dx;
1476       cx = -dx;
1477       dx = 0;
1478     }
1479     else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1480       width -= dx;
1481     else if (dx)                /* general horizontal movement */
1482       MarkTileDirty(x + SIGN(dx), y);
1483
1484     if (y < BY1)                /* object enters playfield from the top */
1485     {
1486       if (cut_mode == CUT_BELOW) /* object completely above top border */
1487         return;
1488
1489       y = BY1;
1490       height = dy;
1491       cy = TILEY - dy;
1492       dy = 0;
1493     }
1494     else if (y > BY2)           /* object enters playfield from the bottom */
1495     {
1496       y = BY2;
1497       height = -dy;
1498       dy = TILEY + dy;
1499     }
1500     else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1501     {
1502       height += dy;
1503       cy = -dy;
1504       dy = 0;
1505     }
1506     else if (dy > 0 && cut_mode == CUT_ABOVE)
1507     {
1508       if (y == BY2)             /* object completely above bottom border */
1509         return;
1510
1511       height = dy;
1512       cy = TILEY - dy;
1513       dy = TILEY;
1514       MarkTileDirty(x, y + 1);
1515     }                           /* object leaves playfield to the bottom */
1516     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1517       height -= dy;
1518     else if (dy)                /* general vertical movement */
1519       MarkTileDirty(x, y + SIGN(dy));
1520   }
1521
1522 #if DEBUG
1523   if (!IN_SCR_FIELD(x, y))
1524   {
1525     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1526     printf("DrawGraphicShifted(): This should never happen!\n");
1527     return;
1528   }
1529 #endif
1530
1531   width = width * TILESIZE_VAR / TILESIZE;
1532   height = height * TILESIZE_VAR / TILESIZE;
1533   cx = cx * TILESIZE_VAR / TILESIZE;
1534   cy = cy * TILESIZE_VAR / TILESIZE;
1535   dx = dx * TILESIZE_VAR / TILESIZE;
1536   dy = dy * TILESIZE_VAR / TILESIZE;
1537
1538   if (width > 0 && height > 0)
1539   {
1540     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1541
1542     src_x += cx;
1543     src_y += cy;
1544
1545     dst_x = FX + x * TILEX_VAR + dx;
1546     dst_y = FY + y * TILEY_VAR + dy;
1547
1548     if (mask_mode == USE_MASKING)
1549       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1550                        dst_x, dst_y);
1551     else
1552       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1553                  dst_x, dst_y);
1554
1555     MarkTileDirty(x, y);
1556   }
1557 }
1558
1559 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1560                                             int graphic, int frame,
1561                                             int cut_mode, int mask_mode)
1562 {
1563   Bitmap *src_bitmap;
1564   int src_x, src_y;
1565   int dst_x, dst_y;
1566   int width = TILEX_VAR, height = TILEY_VAR;
1567   int x1 = x;
1568   int y1 = y;
1569   int x2 = x + SIGN(dx);
1570   int y2 = y + SIGN(dy);
1571
1572   /* movement with two-tile animations must be sync'ed with movement position,
1573      not with current GfxFrame (which can be higher when using slow movement) */
1574   int anim_pos = (dx ? ABS(dx) : ABS(dy));
1575   int anim_frames = graphic_info[graphic].anim_frames;
1576
1577   /* (we also need anim_delay here for movement animations with less frames) */
1578   int anim_delay = graphic_info[graphic].anim_delay;
1579   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1580
1581   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    /* only for falling! */
1582   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    /* only for falling! */
1583
1584   /* re-calculate animation frame for two-tile movement animation */
1585   frame = getGraphicAnimationFrame(graphic, sync_frame);
1586
1587   /* check if movement start graphic inside screen area and should be drawn */
1588   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1589   {
1590     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1591
1592     dst_x = FX + x1 * TILEX_VAR;
1593     dst_y = FY + y1 * TILEY_VAR;
1594
1595     if (mask_mode == USE_MASKING)
1596       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1597                        dst_x, dst_y);
1598     else
1599       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1600                  dst_x, dst_y);
1601
1602     MarkTileDirty(x1, y1);
1603   }
1604
1605   /* check if movement end graphic inside screen area and should be drawn */
1606   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1607   {
1608     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1609
1610     dst_x = FX + x2 * TILEX_VAR;
1611     dst_y = FY + y2 * TILEY_VAR;
1612
1613     if (mask_mode == USE_MASKING)
1614       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1615                        dst_x, dst_y);
1616     else
1617       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1618                  dst_x, dst_y);
1619
1620     MarkTileDirty(x2, y2);
1621   }
1622 }
1623
1624 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1625                                int graphic, int frame,
1626                                int cut_mode, int mask_mode)
1627 {
1628   if (graphic < 0)
1629   {
1630     DrawGraphic(x, y, graphic, frame);
1631
1632     return;
1633   }
1634
1635   if (graphic_info[graphic].double_movement)    /* EM style movement images */
1636     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1637   else
1638     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1639 }
1640
1641 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1642                                 int frame, int cut_mode)
1643 {
1644   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1645 }
1646
1647 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1648                           int cut_mode, int mask_mode)
1649 {
1650   int lx = LEVELX(x), ly = LEVELY(y);
1651   int graphic;
1652   int frame;
1653
1654   if (IN_LEV_FIELD(lx, ly))
1655   {
1656     SetRandomAnimationValue(lx, ly);
1657
1658     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1659     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1660
1661     /* do not use double (EM style) movement graphic when not moving */
1662     if (graphic_info[graphic].double_movement && !dx && !dy)
1663     {
1664       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1665       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1666     }
1667   }
1668   else  /* border element */
1669   {
1670     graphic = el2img(element);
1671     frame = getGraphicAnimationFrame(graphic, -1);
1672   }
1673
1674   if (element == EL_EXPANDABLE_WALL)
1675   {
1676     boolean left_stopped = FALSE, right_stopped = FALSE;
1677
1678     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1679       left_stopped = TRUE;
1680     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1681       right_stopped = TRUE;
1682
1683     if (left_stopped && right_stopped)
1684       graphic = IMG_WALL;
1685     else if (left_stopped)
1686     {
1687       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1688       frame = graphic_info[graphic].anim_frames - 1;
1689     }
1690     else if (right_stopped)
1691     {
1692       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1693       frame = graphic_info[graphic].anim_frames - 1;
1694     }
1695   }
1696
1697   if (dx || dy)
1698     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1699   else if (mask_mode == USE_MASKING)
1700     DrawGraphicThruMask(x, y, graphic, frame);
1701   else
1702     DrawGraphic(x, y, graphic, frame);
1703 }
1704
1705 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1706                          int cut_mode, int mask_mode)
1707 {
1708   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1709     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1710                          cut_mode, mask_mode);
1711 }
1712
1713 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1714                               int cut_mode)
1715 {
1716   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1717 }
1718
1719 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1720                              int cut_mode)
1721 {
1722   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1723 }
1724
1725 void DrawLevelElementThruMask(int x, int y, int element)
1726 {
1727   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1728 }
1729
1730 void DrawLevelFieldThruMask(int x, int y)
1731 {
1732   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1733 }
1734
1735 /* !!! implementation of quicksand is totally broken !!! */
1736 #define IS_CRUMBLED_TILE(x, y, e)                                       \
1737         (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
1738                              !IS_MOVING(x, y) ||                        \
1739                              (e) == EL_QUICKSAND_EMPTYING ||            \
1740                              (e) == EL_QUICKSAND_FAST_EMPTYING))
1741
1742 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1743                                                int graphic)
1744 {
1745   Bitmap *src_bitmap;
1746   int src_x, src_y;
1747   int width, height, cx, cy;
1748   int sx = SCREENX(x), sy = SCREENY(y);
1749   int crumbled_border_size = graphic_info[graphic].border_size;
1750   int i;
1751
1752   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1753
1754   for (i = 1; i < 4; i++)
1755   {
1756     int dxx = (i & 1 ? dx : 0);
1757     int dyy = (i & 2 ? dy : 0);
1758     int xx = x + dxx;
1759     int yy = y + dyy;
1760     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1761                    BorderElement);
1762
1763     /* check if neighbour field is of same crumble type */
1764     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1765                     graphic_info[graphic].class ==
1766                     graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1767
1768     /* return if check prevents inner corner */
1769     if (same == (dxx == dx && dyy == dy))
1770       return;
1771   }
1772
1773   /* if we reach this point, we have an inner corner */
1774
1775   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1776
1777   width  = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1778   height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1779   cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
1780   cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1781
1782   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1783              width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1784 }
1785
1786 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1787                                           int dir)
1788 {
1789   Bitmap *src_bitmap;
1790   int src_x, src_y;
1791   int width, height, bx, by, cx, cy;
1792   int sx = SCREENX(x), sy = SCREENY(y);
1793   int crumbled_border_size = graphic_info[graphic].border_size;
1794   int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1795   int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1796   int i;
1797
1798   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1799
1800   /* draw simple, sloppy, non-corner-accurate crumbled border */
1801
1802   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1803   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1804   cx = (dir == 2 ? crumbled_border_pos_var : 0);
1805   cy = (dir == 3 ? crumbled_border_pos_var : 0);
1806
1807   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1808              FX + sx * TILEX_VAR + cx,
1809              FY + sy * TILEY_VAR + cy);
1810
1811   /* (remaining middle border part must be at least as big as corner part) */
1812   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1813       crumbled_border_size >= TILESIZE / 3)
1814     return;
1815
1816   /* correct corners of crumbled border, if needed */
1817
1818   for (i = -1; i <= 1; i += 2)
1819   {
1820     int xx = x + (dir == 0 || dir == 3 ? i : 0);
1821     int yy = y + (dir == 1 || dir == 2 ? i : 0);
1822     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1823                    BorderElement);
1824
1825     /* check if neighbour field is of same crumble type */
1826     if (IS_CRUMBLED_TILE(xx, yy, element) &&
1827         graphic_info[graphic].class ==
1828         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1829     {
1830       /* no crumbled corner, but continued crumbled border */
1831
1832       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1833       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1834       int b1 = (i == 1 ? crumbled_border_size_var :
1835                 TILESIZE_VAR - 2 * crumbled_border_size_var);
1836
1837       width  = crumbled_border_size_var;
1838       height = crumbled_border_size_var;
1839
1840       if (dir == 1 || dir == 2)
1841       {
1842         cx = c1;
1843         cy = c2;
1844         bx = cx;
1845         by = b1;
1846       }
1847       else
1848       {
1849         cx = c2;
1850         cy = c1;
1851         bx = b1;
1852         by = cy;
1853       }
1854
1855       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1856                  width, height,
1857                  FX + sx * TILEX_VAR + cx,
1858                  FY + sy * TILEY_VAR + cy);
1859     }
1860   }
1861 }
1862
1863 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1864 {
1865   int sx = SCREENX(x), sy = SCREENY(y);
1866   int element;
1867   int i;
1868   static int xy[4][2] =
1869   {
1870     { 0, -1 },
1871     { -1, 0 },
1872     { +1, 0 },
1873     { 0, +1 }
1874   };
1875
1876   if (!IN_LEV_FIELD(x, y))
1877     return;
1878
1879   element = TILE_GFX_ELEMENT(x, y);
1880
1881   /* crumble field itself */
1882   if (IS_CRUMBLED_TILE(x, y, element))
1883   {
1884     if (!IN_SCR_FIELD(sx, sy))
1885       return;
1886
1887     for (i = 0; i < 4; i++)
1888     {
1889       int xx = x + xy[i][0];
1890       int yy = y + xy[i][1];
1891
1892       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1893                  BorderElement);
1894
1895       /* check if neighbour field is of same crumble type */
1896       if (IS_CRUMBLED_TILE(xx, yy, element) &&
1897           graphic_info[graphic].class ==
1898           graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1899         continue;
1900
1901       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1902     }
1903
1904     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1905         graphic_info[graphic].anim_frames == 2)
1906     {
1907       for (i = 0; i < 4; i++)
1908       {
1909         int dx = (i & 1 ? +1 : -1);
1910         int dy = (i & 2 ? +1 : -1);
1911
1912         DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1913       }
1914     }
1915
1916     MarkTileDirty(sx, sy);
1917   }
1918   else          /* center field not crumbled -- crumble neighbour fields */
1919   {
1920     for (i = 0; i < 4; i++)
1921     {
1922       int xx = x + xy[i][0];
1923       int yy = y + xy[i][1];
1924       int sxx = sx + xy[i][0];
1925       int syy = sy + xy[i][1];
1926
1927       if (!IN_LEV_FIELD(xx, yy) ||
1928           !IN_SCR_FIELD(sxx, syy))
1929         continue;
1930
1931       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1932         continue;
1933
1934       element = TILE_GFX_ELEMENT(xx, yy);
1935
1936       if (!IS_CRUMBLED_TILE(xx, yy, element))
1937         continue;
1938
1939       graphic = el_act2crm(element, ACTION_DEFAULT);
1940
1941       DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1942
1943       MarkTileDirty(sxx, syy);
1944     }
1945   }
1946 }
1947
1948 void DrawLevelFieldCrumbled(int x, int y)
1949 {
1950   int graphic;
1951
1952   if (!IN_LEV_FIELD(x, y))
1953     return;
1954
1955   if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1956       GfxElement[x][y] != EL_UNDEFINED &&
1957       GFX_CRUMBLED(GfxElement[x][y]))
1958   {
1959     DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1960
1961     return;
1962   }
1963
1964   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1965
1966   DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1967 }
1968
1969 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1970                                    int step_frame)
1971 {
1972   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1973   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1974   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1975   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1976   int sx = SCREENX(x), sy = SCREENY(y);
1977
1978   DrawGraphic(sx, sy, graphic1, frame1);
1979   DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1980 }
1981
1982 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1983 {
1984   int sx = SCREENX(x), sy = SCREENY(y);
1985   static int xy[4][2] =
1986   {
1987     { 0, -1 },
1988     { -1, 0 },
1989     { +1, 0 },
1990     { 0, +1 }
1991   };
1992   int i;
1993
1994   for (i = 0; i < 4; i++)
1995   {
1996     int xx = x + xy[i][0];
1997     int yy = y + xy[i][1];
1998     int sxx = sx + xy[i][0];
1999     int syy = sy + xy[i][1];
2000
2001     if (!IN_LEV_FIELD(xx, yy) ||
2002         !IN_SCR_FIELD(sxx, syy) ||
2003         !GFX_CRUMBLED(Feld[xx][yy]) ||
2004         IS_MOVING(xx, yy))
2005       continue;
2006
2007     DrawLevelField(xx, yy);
2008   }
2009 }
2010
2011 static int getBorderElement(int x, int y)
2012 {
2013   int border[7][2] =
2014   {
2015     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
2016     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
2017     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
2018     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2019     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
2020     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
2021     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
2022   };
2023   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2024   int steel_position = (x == -1         && y == -1              ? 0 :
2025                         x == lev_fieldx && y == -1              ? 1 :
2026                         x == -1         && y == lev_fieldy      ? 2 :
2027                         x == lev_fieldx && y == lev_fieldy      ? 3 :
2028                         x == -1         || x == lev_fieldx      ? 4 :
2029                         y == -1         || y == lev_fieldy      ? 5 : 6);
2030
2031   return border[steel_position][steel_type];
2032 }
2033
2034 void DrawScreenElement(int x, int y, int element)
2035 {
2036   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2037   DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2038 }
2039
2040 void DrawLevelElement(int x, int y, int element)
2041 {
2042   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2043     DrawScreenElement(SCREENX(x), SCREENY(y), element);
2044 }
2045
2046 void DrawScreenField(int x, int y)
2047 {
2048   int lx = LEVELX(x), ly = LEVELY(y);
2049   int element, content;
2050
2051   if (!IN_LEV_FIELD(lx, ly))
2052   {
2053     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2054       element = EL_EMPTY;
2055     else
2056       element = getBorderElement(lx, ly);
2057
2058     DrawScreenElement(x, y, element);
2059
2060     return;
2061   }
2062
2063   element = Feld[lx][ly];
2064   content = Store[lx][ly];
2065
2066   if (IS_MOVING(lx, ly))
2067   {
2068     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2069     boolean cut_mode = NO_CUTTING;
2070
2071     if (element == EL_QUICKSAND_EMPTYING ||
2072         element == EL_QUICKSAND_FAST_EMPTYING ||
2073         element == EL_MAGIC_WALL_EMPTYING ||
2074         element == EL_BD_MAGIC_WALL_EMPTYING ||
2075         element == EL_DC_MAGIC_WALL_EMPTYING ||
2076         element == EL_AMOEBA_DROPPING)
2077       cut_mode = CUT_ABOVE;
2078     else if (element == EL_QUICKSAND_FILLING ||
2079              element == EL_QUICKSAND_FAST_FILLING ||
2080              element == EL_MAGIC_WALL_FILLING ||
2081              element == EL_BD_MAGIC_WALL_FILLING ||
2082              element == EL_DC_MAGIC_WALL_FILLING)
2083       cut_mode = CUT_BELOW;
2084
2085     if (cut_mode == CUT_ABOVE)
2086       DrawScreenElement(x, y, element);
2087     else
2088       DrawScreenElement(x, y, EL_EMPTY);
2089
2090     if (horiz_move)
2091       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2092     else if (cut_mode == NO_CUTTING)
2093       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2094     else
2095     {
2096       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2097
2098       if (cut_mode == CUT_BELOW &&
2099           IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2100         DrawLevelElement(lx, ly + 1, element);
2101     }
2102
2103     if (content == EL_ACID)
2104     {
2105       int dir = MovDir[lx][ly];
2106       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2107       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2108
2109       DrawLevelElementThruMask(newlx, newly, EL_ACID);
2110     }
2111   }
2112   else if (IS_BLOCKED(lx, ly))
2113   {
2114     int oldx, oldy;
2115     int sx, sy;
2116     int horiz_move;
2117     boolean cut_mode = NO_CUTTING;
2118     int element_old, content_old;
2119
2120     Blocked2Moving(lx, ly, &oldx, &oldy);
2121     sx = SCREENX(oldx);
2122     sy = SCREENY(oldy);
2123     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2124                   MovDir[oldx][oldy] == MV_RIGHT);
2125
2126     element_old = Feld[oldx][oldy];
2127     content_old = Store[oldx][oldy];
2128
2129     if (element_old == EL_QUICKSAND_EMPTYING ||
2130         element_old == EL_QUICKSAND_FAST_EMPTYING ||
2131         element_old == EL_MAGIC_WALL_EMPTYING ||
2132         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2133         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2134         element_old == EL_AMOEBA_DROPPING)
2135       cut_mode = CUT_ABOVE;
2136
2137     DrawScreenElement(x, y, EL_EMPTY);
2138
2139     if (horiz_move)
2140       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2141                                NO_CUTTING);
2142     else if (cut_mode == NO_CUTTING)
2143       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2144                                cut_mode);
2145     else
2146       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2147                                cut_mode);
2148   }
2149   else if (IS_DRAWABLE(element))
2150     DrawScreenElement(x, y, element);
2151   else
2152     DrawScreenElement(x, y, EL_EMPTY);
2153 }
2154
2155 void DrawLevelField(int x, int y)
2156 {
2157   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2158     DrawScreenField(SCREENX(x), SCREENY(y));
2159   else if (IS_MOVING(x, y))
2160   {
2161     int newx,newy;
2162
2163     Moving2Blocked(x, y, &newx, &newy);
2164     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2165       DrawScreenField(SCREENX(newx), SCREENY(newy));
2166   }
2167   else if (IS_BLOCKED(x, y))
2168   {
2169     int oldx, oldy;
2170
2171     Blocked2Moving(x, y, &oldx, &oldy);
2172     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2173       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2174   }
2175 }
2176
2177 void DrawSizedElement(int x, int y, int element, int tilesize)
2178 {
2179   int graphic;
2180
2181   graphic = el2edimg(element);
2182   DrawSizedGraphic(x, y, graphic, 0, tilesize);
2183 }
2184
2185 void DrawMiniElement(int x, int y, int element)
2186 {
2187   int graphic;
2188
2189   graphic = el2edimg(element);
2190   DrawMiniGraphic(x, y, graphic);
2191 }
2192
2193 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2194                             int tilesize)
2195 {
2196   int x = sx + scroll_x, y = sy + scroll_y;
2197
2198   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2199     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2200   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2201     DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2202   else
2203     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2204 }
2205
2206 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2207 {
2208   int x = sx + scroll_x, y = sy + scroll_y;
2209
2210   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2211     DrawMiniElement(sx, sy, EL_EMPTY);
2212   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2213     DrawMiniElement(sx, sy, Feld[x][y]);
2214   else
2215     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2216 }
2217
2218 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2219                                  int x, int y, int xsize, int ysize,
2220                                  int tile_width, int tile_height)
2221 {
2222   Bitmap *src_bitmap;
2223   int src_x, src_y;
2224   int dst_x = startx + x * tile_width;
2225   int dst_y = starty + y * tile_height;
2226   int width  = graphic_info[graphic].width;
2227   int height = graphic_info[graphic].height;
2228   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2229   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2230   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2231   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2232   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2233   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2234   boolean draw_masked = graphic_info[graphic].draw_masked;
2235
2236   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2237
2238   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2239   {
2240     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2241     return;
2242   }
2243
2244   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2245             inner_sx + (x - 1) * tile_width  % inner_width);
2246   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2247             inner_sy + (y - 1) * tile_height % inner_height);
2248
2249   if (draw_masked)
2250     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2251                      dst_x, dst_y);
2252   else
2253     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2254                dst_x, dst_y);
2255 }
2256
2257 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2258                             int x, int y, int xsize, int ysize, int font_nr)
2259 {
2260   int font_width  = getFontWidth(font_nr);
2261   int font_height = getFontHeight(font_nr);
2262
2263   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2264                               font_width, font_height);
2265 }
2266
2267 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2268 {
2269   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2270   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2271   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2272   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2273   boolean no_delay = (tape.warp_forward);
2274   unsigned int anim_delay = 0;
2275   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2276   int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2277   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2278   int font_width = getFontWidth(font_nr);
2279   int font_height = getFontHeight(font_nr);
2280   int max_xsize = level.envelope[envelope_nr].xsize;
2281   int max_ysize = level.envelope[envelope_nr].ysize;
2282   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2283   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2284   int xend = max_xsize;
2285   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2286   int xstep = (xstart < xend ? 1 : 0);
2287   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2288   int start = 0;
2289   int end = MAX(xend - xstart, yend - ystart);
2290   int i;
2291
2292   for (i = start; i <= end; i++)
2293   {
2294     int last_frame = end;       // last frame of this "for" loop
2295     int x = xstart + i * xstep;
2296     int y = ystart + i * ystep;
2297     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2298     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2299     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2300     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2301     int xx, yy;
2302
2303     SetDrawtoField(DRAW_FIELDBUFFER);
2304
2305     BlitScreenToBitmap(backbuffer);
2306
2307     SetDrawtoField(DRAW_BACKBUFFER);
2308
2309     for (yy = 0; yy < ysize; yy++)
2310       for (xx = 0; xx < xsize; xx++)
2311         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2312
2313     DrawTextBuffer(sx + font_width, sy + font_height,
2314                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2315                    xsize - 2, ysize - 2, 0, mask_mode,
2316                    level.envelope[envelope_nr].autowrap,
2317                    level.envelope[envelope_nr].centered, FALSE);
2318
2319     redraw_mask |= REDRAW_FIELD;
2320     BackToFront();
2321
2322     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2323   }
2324 }
2325
2326 void ShowEnvelope(int envelope_nr)
2327 {
2328   int element = EL_ENVELOPE_1 + envelope_nr;
2329   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2330   int sound_opening = element_info[element].sound[ACTION_OPENING];
2331   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2332   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2333   boolean no_delay = (tape.warp_forward);
2334   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2335   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2336   int anim_mode = graphic_info[graphic].anim_mode;
2337   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2338                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2339
2340   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2341
2342   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2343
2344   if (anim_mode == ANIM_DEFAULT)
2345     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2346
2347   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2348
2349   if (tape.playing)
2350     Delay(wait_delay_value);
2351   else
2352     WaitForEventToContinue();
2353
2354   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2355
2356   if (anim_mode != ANIM_NONE)
2357     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2358
2359   if (anim_mode == ANIM_DEFAULT)
2360     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2361
2362   game.envelope_active = FALSE;
2363
2364   SetDrawtoField(DRAW_FIELDBUFFER);
2365
2366   redraw_mask |= REDRAW_FIELD;
2367   BackToFront();
2368 }
2369
2370 static void setRequestBasePosition(int *x, int *y)
2371 {
2372   int sx_base, sy_base;
2373
2374   if (request.x != -1)
2375     sx_base = request.x;
2376   else if (request.align == ALIGN_LEFT)
2377     sx_base = SX;
2378   else if (request.align == ALIGN_RIGHT)
2379     sx_base = SX + SXSIZE;
2380   else
2381     sx_base = SX + SXSIZE / 2;
2382
2383   if (request.y != -1)
2384     sy_base = request.y;
2385   else if (request.valign == VALIGN_TOP)
2386     sy_base = SY;
2387   else if (request.valign == VALIGN_BOTTOM)
2388     sy_base = SY + SYSIZE;
2389   else
2390     sy_base = SY + SYSIZE / 2;
2391
2392   *x = sx_base;
2393   *y = sy_base;
2394 }
2395
2396 static void setRequestPositionExt(int *x, int *y, int width, int height,
2397                                   boolean add_border_size)
2398 {
2399   int border_size = request.border_size;
2400   int sx_base, sy_base;
2401   int sx, sy;
2402
2403   setRequestBasePosition(&sx_base, &sy_base);
2404
2405   if (request.align == ALIGN_LEFT)
2406     sx = sx_base;
2407   else if (request.align == ALIGN_RIGHT)
2408     sx = sx_base - width;
2409   else
2410     sx = sx_base - width  / 2;
2411
2412   if (request.valign == VALIGN_TOP)
2413     sy = sy_base;
2414   else if (request.valign == VALIGN_BOTTOM)
2415     sy = sy_base - height;
2416   else
2417     sy = sy_base - height / 2;
2418
2419   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2420   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2421
2422   if (add_border_size)
2423   {
2424     sx += border_size;
2425     sy += border_size;
2426   }
2427
2428   *x = sx;
2429   *y = sy;
2430 }
2431
2432 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2433 {
2434   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2435 }
2436
2437 void DrawEnvelopeRequest(char *text)
2438 {
2439   char *text_final = text;
2440   char *text_door_style = NULL;
2441   int graphic = IMG_BACKGROUND_REQUEST;
2442   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2443   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2444   int font_nr = FONT_REQUEST;
2445   int font_width = getFontWidth(font_nr);
2446   int font_height = getFontHeight(font_nr);
2447   int border_size = request.border_size;
2448   int line_spacing = request.line_spacing;
2449   int line_height = font_height + line_spacing;
2450   int max_text_width  = request.width  - 2 * border_size;
2451   int max_text_height = request.height - 2 * border_size;
2452   int line_length = max_text_width  / font_width;
2453   int max_lines   = max_text_height / line_height;
2454   int text_width = line_length * font_width;
2455   int width = request.width;
2456   int height = request.height;
2457   int tile_size = MAX(request.step_offset, 1);
2458   int x_steps = width  / tile_size;
2459   int y_steps = height / tile_size;
2460   int sx_offset = border_size;
2461   int sy_offset = border_size;
2462   int sx, sy;
2463   int i, x, y;
2464
2465   if (request.centered)
2466     sx_offset = (request.width - text_width) / 2;
2467
2468   if (request.wrap_single_words && !request.autowrap)
2469   {
2470     char *src_text_ptr, *dst_text_ptr;
2471
2472     text_door_style = checked_malloc(2 * strlen(text) + 1);
2473
2474     src_text_ptr = text;
2475     dst_text_ptr = text_door_style;
2476
2477     while (*src_text_ptr)
2478     {
2479       if (*src_text_ptr == ' ' ||
2480           *src_text_ptr == '?' ||
2481           *src_text_ptr == '!')
2482         *dst_text_ptr++ = '\n';
2483
2484       if (*src_text_ptr != ' ')
2485         *dst_text_ptr++ = *src_text_ptr;
2486
2487       src_text_ptr++;
2488     }
2489
2490     *dst_text_ptr = '\0';
2491
2492     text_final = text_door_style;
2493   }
2494
2495   setRequestPosition(&sx, &sy, FALSE);
2496
2497   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2498
2499   for (y = 0; y < y_steps; y++)
2500     for (x = 0; x < x_steps; x++)
2501       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2502                                   x, y, x_steps, y_steps,
2503                                   tile_size, tile_size);
2504
2505   /* force DOOR font inside door area */
2506   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2507
2508   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2509                  line_length, -1, max_lines, line_spacing, mask_mode,
2510                  request.autowrap, request.centered, FALSE);
2511
2512   ResetFontStatus();
2513
2514   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2515     RedrawGadget(tool_gadget[i]);
2516
2517   // store readily prepared envelope request for later use when animating
2518   BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2519
2520   if (text_door_style)
2521     free(text_door_style);
2522 }
2523
2524 void AnimateEnvelopeRequest(int anim_mode, int action)
2525 {
2526   int graphic = IMG_BACKGROUND_REQUEST;
2527   boolean draw_masked = graphic_info[graphic].draw_masked;
2528   int delay_value_normal = request.step_delay;
2529   int delay_value_fast = delay_value_normal / 2;
2530   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2531   boolean no_delay = (tape.warp_forward);
2532   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2533   int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2534   unsigned int anim_delay = 0;
2535
2536   int tile_size = MAX(request.step_offset, 1);
2537   int max_xsize = request.width  / tile_size;
2538   int max_ysize = request.height / tile_size;
2539   int max_xsize_inner = max_xsize - 2;
2540   int max_ysize_inner = max_ysize - 2;
2541
2542   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2543   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2544   int xend = max_xsize_inner;
2545   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2546   int xstep = (xstart < xend ? 1 : 0);
2547   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2548   int start = 0;
2549   int end = MAX(xend - xstart, yend - ystart);
2550   int i;
2551
2552   if (setup.quick_doors)
2553   {
2554     xstart = xend;
2555     ystart = yend;
2556     end = 0;
2557   }
2558
2559   for (i = start; i <= end; i++)
2560   {
2561     int last_frame = end;       // last frame of this "for" loop
2562     int x = xstart + i * xstep;
2563     int y = ystart + i * ystep;
2564     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2565     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2566     int xsize_size_left = (xsize - 1) * tile_size;
2567     int ysize_size_top  = (ysize - 1) * tile_size;
2568     int max_xsize_pos = (max_xsize - 1) * tile_size;
2569     int max_ysize_pos = (max_ysize - 1) * tile_size;
2570     int width  = xsize * tile_size;
2571     int height = ysize * tile_size;
2572     int src_x, src_y;
2573     int dst_x, dst_y;
2574     int xx, yy;
2575
2576     setRequestPosition(&src_x, &src_y, FALSE);
2577     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2578
2579     BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2580
2581     for (yy = 0; yy < 2; yy++)
2582     {
2583       for (xx = 0; xx < 2; xx++)
2584       {
2585         int src_xx = src_x + xx * max_xsize_pos;
2586         int src_yy = src_y + yy * max_ysize_pos;
2587         int dst_xx = dst_x + xx * xsize_size_left;
2588         int dst_yy = dst_y + yy * ysize_size_top;
2589         int xx_size = (xx ? tile_size : xsize_size_left);
2590         int yy_size = (yy ? tile_size : ysize_size_top);
2591
2592         if (draw_masked)
2593           BlitBitmapMasked(bitmap_db_cross, backbuffer,
2594                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2595         else
2596           BlitBitmap(bitmap_db_cross, backbuffer,
2597                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2598       }
2599     }
2600
2601     redraw_mask |= REDRAW_FIELD;
2602
2603     DoAnimation();
2604     BackToFront();
2605
2606     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2607   }
2608 }
2609
2610 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2611 {
2612   int graphic = IMG_BACKGROUND_REQUEST;
2613   int sound_opening = SND_REQUEST_OPENING;
2614   int sound_closing = SND_REQUEST_CLOSING;
2615   int anim_mode_1 = request.anim_mode;                  /* (higher priority) */
2616   int anim_mode_2 = graphic_info[graphic].anim_mode;    /* (lower priority) */
2617   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2618   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2619                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2620
2621   if (game_status == GAME_MODE_PLAYING)
2622     BlitScreenToBitmap(backbuffer);
2623
2624   SetDrawtoField(DRAW_BACKBUFFER);
2625
2626   // SetDrawBackgroundMask(REDRAW_NONE);
2627
2628   if (action == ACTION_OPENING)
2629   {
2630     BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2631
2632     if (req_state & REQ_ASK)
2633     {
2634       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2635       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2636     }
2637     else if (req_state & REQ_CONFIRM)
2638     {
2639       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2640     }
2641     else if (req_state & REQ_PLAYER)
2642     {
2643       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2644       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2645       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2646       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2647     }
2648
2649     DrawEnvelopeRequest(text);
2650
2651     if (game_status != GAME_MODE_MAIN)
2652       InitAnimation();
2653   }
2654
2655   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2656
2657   if (action == ACTION_OPENING)
2658   {
2659     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2660
2661     if (anim_mode == ANIM_DEFAULT)
2662       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2663
2664     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2665   }
2666   else
2667   {
2668     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2669
2670     if (anim_mode != ANIM_NONE)
2671       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2672
2673     if (anim_mode == ANIM_DEFAULT)
2674       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2675   }
2676
2677   game.envelope_active = FALSE;
2678
2679   if (action == ACTION_CLOSING)
2680   {
2681     if (game_status != GAME_MODE_MAIN)
2682       StopAnimation();
2683
2684     BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2685   }
2686
2687   // SetDrawBackgroundMask(last_draw_background_mask);
2688
2689   redraw_mask |= REDRAW_FIELD;
2690
2691   if (game_status == GAME_MODE_MAIN)
2692     DoAnimation();
2693
2694   BackToFront();
2695
2696   if (action == ACTION_CLOSING &&
2697       game_status == GAME_MODE_PLAYING &&
2698       level.game_engine_type == GAME_ENGINE_TYPE_RND)
2699     SetDrawtoField(DRAW_FIELDBUFFER);
2700 }
2701
2702 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2703 {
2704   Bitmap *src_bitmap;
2705   int src_x, src_y;
2706   int graphic = el2preimg(element);
2707
2708   getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2709   BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2710 }
2711
2712 void DrawLevel(int draw_background_mask)
2713 {
2714   int x,y;
2715
2716   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2717   SetDrawBackgroundMask(draw_background_mask);
2718
2719   ClearField();
2720
2721   for (x = BX1; x <= BX2; x++)
2722     for (y = BY1; y <= BY2; y++)
2723       DrawScreenField(x, y);
2724
2725   redraw_mask |= REDRAW_FIELD;
2726 }
2727
2728 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2729                     int tilesize)
2730 {
2731   int x,y;
2732
2733   for (x = 0; x < size_x; x++)
2734     for (y = 0; y < size_y; y++)
2735       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2736
2737   redraw_mask |= REDRAW_FIELD;
2738 }
2739
2740 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2741 {
2742   int x,y;
2743
2744   for (x = 0; x < size_x; x++)
2745     for (y = 0; y < size_y; y++)
2746       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2747
2748   redraw_mask |= REDRAW_FIELD;
2749 }
2750
2751 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2752 {
2753   boolean show_level_border = (BorderElement != EL_EMPTY);
2754   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2755   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2756   int tile_size = preview.tile_size;
2757   int preview_width  = preview.xsize * tile_size;
2758   int preview_height = preview.ysize * tile_size;
2759   int real_preview_xsize = MIN(level_xsize, preview.xsize);
2760   int real_preview_ysize = MIN(level_ysize, preview.ysize);
2761   int real_preview_width  = real_preview_xsize * tile_size;
2762   int real_preview_height = real_preview_ysize * tile_size;
2763   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2764   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2765   int x, y;
2766
2767   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2768     return;
2769
2770   DrawBackground(dst_x, dst_y, preview_width, preview_height);
2771
2772   dst_x += (preview_width  - real_preview_width)  / 2;
2773   dst_y += (preview_height - real_preview_height) / 2;
2774
2775   for (x = 0; x < real_preview_xsize; x++)
2776   {
2777     for (y = 0; y < real_preview_ysize; y++)
2778     {
2779       int lx = from_x + x + (show_level_border ? -1 : 0);
2780       int ly = from_y + y + (show_level_border ? -1 : 0);
2781       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2782                      getBorderElement(lx, ly));
2783
2784       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2785                          element, tile_size);
2786     }
2787   }
2788
2789   redraw_mask |= REDRAW_FIELD;
2790 }
2791
2792 #define MICROLABEL_EMPTY                0
2793 #define MICROLABEL_LEVEL_NAME           1
2794 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
2795 #define MICROLABEL_LEVEL_AUTHOR         3
2796 #define MICROLABEL_IMPORTED_FROM_HEAD   4
2797 #define MICROLABEL_IMPORTED_FROM        5
2798 #define MICROLABEL_IMPORTED_BY_HEAD     6
2799 #define MICROLABEL_IMPORTED_BY          7
2800
2801 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2802 {
2803   int max_text_width = SXSIZE;
2804   int font_width = getFontWidth(font_nr);
2805
2806   if (pos->align == ALIGN_CENTER)
2807     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2808   else if (pos->align == ALIGN_RIGHT)
2809     max_text_width = pos->x;
2810   else
2811     max_text_width = SXSIZE - pos->x;
2812
2813   return max_text_width / font_width;
2814 }
2815
2816 static void DrawPreviewLevelLabelExt(int mode)
2817 {
2818   struct TextPosInfo *pos = &menu.main.text.level_info_2;
2819   char label_text[MAX_OUTPUT_LINESIZE + 1];
2820   int max_len_label_text;
2821   int font_nr = pos->font;
2822   int i;
2823
2824   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2825     return;
2826
2827   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2828       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2829       mode == MICROLABEL_IMPORTED_BY_HEAD)
2830     font_nr = pos->font_alt;
2831
2832   max_len_label_text = getMaxTextLength(pos, font_nr);
2833
2834   if (pos->size != -1)
2835     max_len_label_text = pos->size;
2836
2837   for (i = 0; i < max_len_label_text; i++)
2838     label_text[i] = ' ';
2839   label_text[max_len_label_text] = '\0';
2840
2841   if (strlen(label_text) > 0)
2842     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2843
2844   strncpy(label_text,
2845           (mode == MICROLABEL_LEVEL_NAME ? level.name :
2846            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2847            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2848            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2849            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2850            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2851            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2852           max_len_label_text);
2853   label_text[max_len_label_text] = '\0';
2854
2855   if (strlen(label_text) > 0)
2856     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2857
2858   redraw_mask |= REDRAW_FIELD;
2859 }
2860
2861 static void DrawPreviewLevelExt(boolean restart)
2862 {
2863   static unsigned int scroll_delay = 0;
2864   static unsigned int label_delay = 0;
2865   static int from_x, from_y, scroll_direction;
2866   static int label_state, label_counter;
2867   unsigned int scroll_delay_value = preview.step_delay;
2868   boolean show_level_border = (BorderElement != EL_EMPTY);
2869   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2870   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2871
2872   if (restart)
2873   {
2874     from_x = 0;
2875     from_y = 0;
2876
2877     if (preview.anim_mode == ANIM_CENTERED)
2878     {
2879       if (level_xsize > preview.xsize)
2880         from_x = (level_xsize - preview.xsize) / 2;
2881       if (level_ysize > preview.ysize)
2882         from_y = (level_ysize - preview.ysize) / 2;
2883     }
2884
2885     from_x += preview.xoffset;
2886     from_y += preview.yoffset;
2887
2888     scroll_direction = MV_RIGHT;
2889     label_state = 1;
2890     label_counter = 0;
2891
2892     DrawPreviewLevelPlayfieldExt(from_x, from_y);
2893     DrawPreviewLevelLabelExt(label_state);
2894
2895     /* initialize delay counters */
2896     DelayReached(&scroll_delay, 0);
2897     DelayReached(&label_delay, 0);
2898
2899     if (leveldir_current->name)
2900     {
2901       struct TextPosInfo *pos = &menu.main.text.level_info_1;
2902       char label_text[MAX_OUTPUT_LINESIZE + 1];
2903       int font_nr = pos->font;
2904       int max_len_label_text = getMaxTextLength(pos, font_nr);
2905
2906       if (pos->size != -1)
2907         max_len_label_text = pos->size;
2908
2909       strncpy(label_text, leveldir_current->name, max_len_label_text);
2910       label_text[max_len_label_text] = '\0';
2911
2912       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2913         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2914     }
2915
2916     return;
2917   }
2918
2919   /* scroll preview level, if needed */
2920   if (preview.anim_mode != ANIM_NONE &&
2921       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2922       DelayReached(&scroll_delay, scroll_delay_value))
2923   {
2924     switch (scroll_direction)
2925     {
2926       case MV_LEFT:
2927         if (from_x > 0)
2928         {
2929           from_x -= preview.step_offset;
2930           from_x = (from_x < 0 ? 0 : from_x);
2931         }
2932         else
2933           scroll_direction = MV_UP;
2934         break;
2935
2936       case MV_RIGHT:
2937         if (from_x < level_xsize - preview.xsize)
2938         {
2939           from_x += preview.step_offset;
2940           from_x = (from_x > level_xsize - preview.xsize ?
2941                     level_xsize - preview.xsize : from_x);
2942         }
2943         else
2944           scroll_direction = MV_DOWN;
2945         break;
2946
2947       case MV_UP:
2948         if (from_y > 0)
2949         {
2950           from_y -= preview.step_offset;
2951           from_y = (from_y < 0 ? 0 : from_y);
2952         }
2953         else
2954           scroll_direction = MV_RIGHT;
2955         break;
2956
2957       case MV_DOWN:
2958         if (from_y < level_ysize - preview.ysize)
2959         {
2960           from_y += preview.step_offset;
2961           from_y = (from_y > level_ysize - preview.ysize ?
2962                     level_ysize - preview.ysize : from_y);
2963         }
2964         else
2965           scroll_direction = MV_LEFT;
2966         break;
2967
2968       default:
2969         break;
2970     }
2971
2972     DrawPreviewLevelPlayfieldExt(from_x, from_y);
2973   }
2974
2975   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2976   /* redraw micro level label, if needed */
2977   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2978       !strEqual(level.author, ANONYMOUS_NAME) &&
2979       !strEqual(level.author, leveldir_current->name) &&
2980       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2981   {
2982     int max_label_counter = 23;
2983
2984     if (leveldir_current->imported_from != NULL &&
2985         strlen(leveldir_current->imported_from) > 0)
2986       max_label_counter += 14;
2987     if (leveldir_current->imported_by != NULL &&
2988         strlen(leveldir_current->imported_by) > 0)
2989       max_label_counter += 14;
2990
2991     label_counter = (label_counter + 1) % max_label_counter;
2992     label_state = (label_counter >= 0 && label_counter <= 7 ?
2993                    MICROLABEL_LEVEL_NAME :
2994                    label_counter >= 9 && label_counter <= 12 ?
2995                    MICROLABEL_LEVEL_AUTHOR_HEAD :
2996                    label_counter >= 14 && label_counter <= 21 ?
2997                    MICROLABEL_LEVEL_AUTHOR :
2998                    label_counter >= 23 && label_counter <= 26 ?
2999                    MICROLABEL_IMPORTED_FROM_HEAD :
3000                    label_counter >= 28 && label_counter <= 35 ?
3001                    MICROLABEL_IMPORTED_FROM :
3002                    label_counter >= 37 && label_counter <= 40 ?
3003                    MICROLABEL_IMPORTED_BY_HEAD :
3004                    label_counter >= 42 && label_counter <= 49 ?
3005                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3006
3007     if (leveldir_current->imported_from == NULL &&
3008         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3009          label_state == MICROLABEL_IMPORTED_FROM))
3010       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3011                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3012
3013     DrawPreviewLevelLabelExt(label_state);
3014   }
3015 }
3016
3017 void DrawPreviewLevelInitial()
3018 {
3019   DrawPreviewLevelExt(TRUE);
3020 }
3021
3022 void DrawPreviewLevelAnimation()
3023 {
3024   DrawPreviewLevelExt(FALSE);
3025 }
3026
3027 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3028                                            int graphic, int sync_frame,
3029                                            int mask_mode)
3030 {
3031   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3032
3033   if (mask_mode == USE_MASKING)
3034     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3035   else
3036     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3037 }
3038
3039 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3040                                   int graphic, int sync_frame, int mask_mode)
3041 {
3042   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3043
3044   if (mask_mode == USE_MASKING)
3045     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3046   else
3047     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3048 }
3049
3050 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3051 {
3052   int lx = LEVELX(x), ly = LEVELY(y);
3053
3054   if (!IN_SCR_FIELD(x, y))
3055     return;
3056
3057   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3058                           graphic, GfxFrame[lx][ly], NO_MASKING);
3059
3060   MarkTileDirty(x, y);
3061 }
3062
3063 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3064 {
3065   int lx = LEVELX(x), ly = LEVELY(y);
3066
3067   if (!IN_SCR_FIELD(x, y))
3068     return;
3069
3070   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3071                           graphic, GfxFrame[lx][ly], NO_MASKING);
3072   MarkTileDirty(x, y);
3073 }
3074
3075 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3076 {
3077   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3078 }
3079
3080 void DrawLevelElementAnimation(int x, int y, int element)
3081 {
3082   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3083
3084   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3085 }
3086
3087 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3088 {
3089   int sx = SCREENX(x), sy = SCREENY(y);
3090
3091   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3092     return;
3093
3094   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3095     return;
3096
3097   DrawGraphicAnimation(sx, sy, graphic);
3098
3099 #if 1
3100   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3101     DrawLevelFieldCrumbled(x, y);
3102 #else
3103   if (GFX_CRUMBLED(Feld[x][y]))
3104     DrawLevelFieldCrumbled(x, y);
3105 #endif
3106 }
3107
3108 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3109 {
3110   int sx = SCREENX(x), sy = SCREENY(y);
3111   int graphic;
3112
3113   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3114     return;
3115
3116   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3117
3118   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3119     return;
3120
3121   DrawGraphicAnimation(sx, sy, graphic);
3122
3123   if (GFX_CRUMBLED(element))
3124     DrawLevelFieldCrumbled(x, y);
3125 }
3126
3127 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3128 {
3129   if (player->use_murphy)
3130   {
3131     /* this works only because currently only one player can be "murphy" ... */
3132     static int last_horizontal_dir = MV_LEFT;
3133     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3134
3135     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3136       last_horizontal_dir = move_dir;
3137
3138     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
3139     {
3140       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3141
3142       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3143     }
3144
3145     return graphic;
3146   }
3147   else
3148     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3149 }
3150
3151 static boolean equalGraphics(int graphic1, int graphic2)
3152 {
3153   struct GraphicInfo *g1 = &graphic_info[graphic1];
3154   struct GraphicInfo *g2 = &graphic_info[graphic2];
3155
3156   return (g1->bitmap      == g2->bitmap &&
3157           g1->src_x       == g2->src_x &&
3158           g1->src_y       == g2->src_y &&
3159           g1->anim_frames == g2->anim_frames &&
3160           g1->anim_delay  == g2->anim_delay &&
3161           g1->anim_mode   == g2->anim_mode);
3162 }
3163
3164 void DrawAllPlayers()
3165 {
3166   int i;
3167
3168   for (i = 0; i < MAX_PLAYERS; i++)
3169     if (stored_player[i].active)
3170       DrawPlayer(&stored_player[i]);
3171 }
3172
3173 void DrawPlayerField(int x, int y)
3174 {
3175   if (!IS_PLAYER(x, y))
3176     return;
3177
3178   DrawPlayer(PLAYERINFO(x, y));
3179 }
3180
3181 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3182
3183 void DrawPlayer(struct PlayerInfo *player)
3184 {
3185   int jx = player->jx;
3186   int jy = player->jy;
3187   int move_dir = player->MovDir;
3188   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3189   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
3190   int last_jx = (player->is_moving ? jx - dx : jx);
3191   int last_jy = (player->is_moving ? jy - dy : jy);
3192   int next_jx = jx + dx;
3193   int next_jy = jy + dy;
3194   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3195   boolean player_is_opaque = FALSE;
3196   int sx = SCREENX(jx), sy = SCREENY(jy);
3197   int sxx = 0, syy = 0;
3198   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3199   int graphic;
3200   int action = ACTION_DEFAULT;
3201   int last_player_graphic = getPlayerGraphic(player, move_dir);
3202   int last_player_frame = player->Frame;
3203   int frame = 0;
3204
3205   /* GfxElement[][] is set to the element the player is digging or collecting;
3206      remove also for off-screen player if the player is not moving anymore */
3207   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3208     GfxElement[jx][jy] = EL_UNDEFINED;
3209
3210   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3211     return;
3212
3213 #if DEBUG
3214   if (!IN_LEV_FIELD(jx, jy))
3215   {
3216     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3217     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3218     printf("DrawPlayerField(): This should never happen!\n");
3219     return;
3220   }
3221 #endif
3222
3223   if (element == EL_EXPLOSION)
3224     return;
3225
3226   action = (player->is_pushing    ? ACTION_PUSHING         :
3227             player->is_digging    ? ACTION_DIGGING         :
3228             player->is_collecting ? ACTION_COLLECTING      :
3229             player->is_moving     ? ACTION_MOVING          :
3230             player->is_snapping   ? ACTION_SNAPPING        :
3231             player->is_dropping   ? ACTION_DROPPING        :
3232             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
3233
3234   if (player->is_waiting)
3235     move_dir = player->dir_waiting;
3236
3237   InitPlayerGfxAnimation(player, action, move_dir);
3238
3239   /* ----------------------------------------------------------------------- */
3240   /* draw things in the field the player is leaving, if needed               */
3241   /* ----------------------------------------------------------------------- */
3242
3243   if (player->is_moving)
3244   {
3245     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3246     {
3247       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3248
3249       if (last_element == EL_DYNAMITE_ACTIVE ||
3250           last_element == EL_EM_DYNAMITE_ACTIVE ||
3251           last_element == EL_SP_DISK_RED_ACTIVE)
3252         DrawDynamite(last_jx, last_jy);
3253       else
3254         DrawLevelFieldThruMask(last_jx, last_jy);
3255     }
3256     else if (last_element == EL_DYNAMITE_ACTIVE ||
3257              last_element == EL_EM_DYNAMITE_ACTIVE ||
3258              last_element == EL_SP_DISK_RED_ACTIVE)
3259       DrawDynamite(last_jx, last_jy);
3260 #if 0
3261     /* !!! this is not enough to prevent flickering of players which are
3262        moving next to each others without a free tile between them -- this
3263        can only be solved by drawing all players layer by layer (first the
3264        background, then the foreground etc.) !!! => TODO */
3265     else if (!IS_PLAYER(last_jx, last_jy))
3266       DrawLevelField(last_jx, last_jy);
3267 #else
3268     else
3269       DrawLevelField(last_jx, last_jy);
3270 #endif
3271
3272     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3273       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3274   }
3275
3276   if (!IN_SCR_FIELD(sx, sy))
3277     return;
3278
3279   /* ----------------------------------------------------------------------- */
3280   /* draw things behind the player, if needed                                */
3281   /* ----------------------------------------------------------------------- */
3282
3283   if (Back[jx][jy])
3284     DrawLevelElement(jx, jy, Back[jx][jy]);
3285   else if (IS_ACTIVE_BOMB(element))
3286     DrawLevelElement(jx, jy, EL_EMPTY);
3287   else
3288   {
3289     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3290     {
3291       int old_element = GfxElement[jx][jy];
3292       int old_graphic = el_act_dir2img(old_element, action, move_dir);
3293       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3294
3295       if (GFX_CRUMBLED(old_element))
3296         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3297       else
3298         DrawGraphic(sx, sy, old_graphic, frame);
3299
3300       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3301         player_is_opaque = TRUE;
3302     }
3303     else
3304     {
3305       GfxElement[jx][jy] = EL_UNDEFINED;
3306
3307       /* make sure that pushed elements are drawn with correct frame rate */
3308       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3309
3310       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3311         GfxFrame[jx][jy] = player->StepFrame;
3312
3313       DrawLevelField(jx, jy);
3314     }
3315   }
3316
3317 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3318   /* ----------------------------------------------------------------------- */
3319   /* draw player himself                                                     */
3320   /* ----------------------------------------------------------------------- */
3321
3322   graphic = getPlayerGraphic(player, move_dir);
3323
3324   /* in the case of changed player action or direction, prevent the current
3325      animation frame from being restarted for identical animations */
3326   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3327     player->Frame = last_player_frame;
3328
3329   frame = getGraphicAnimationFrame(graphic, player->Frame);
3330
3331   if (player->GfxPos)
3332   {
3333     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3334       sxx = player->GfxPos;
3335     else
3336       syy = player->GfxPos;
3337   }
3338
3339   if (player_is_opaque)
3340     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3341   else
3342     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3343
3344   if (SHIELD_ON(player))
3345   {
3346     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3347                    IMG_SHIELD_NORMAL_ACTIVE);
3348     int frame = getGraphicAnimationFrame(graphic, -1);
3349
3350     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3351   }
3352 #endif
3353
3354 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3355   if (player->GfxPos)
3356   {
3357     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3358       sxx = player->GfxPos;
3359     else
3360       syy = player->GfxPos;
3361   }
3362 #endif
3363
3364   /* ----------------------------------------------------------------------- */
3365   /* draw things the player is pushing, if needed                            */
3366   /* ----------------------------------------------------------------------- */
3367
3368   if (player->is_pushing && player->is_moving)
3369   {
3370     int px = SCREENX(jx), py = SCREENY(jy);
3371     int pxx = (TILEX - ABS(sxx)) * dx;
3372     int pyy = (TILEY - ABS(syy)) * dy;
3373     int gfx_frame = GfxFrame[jx][jy];
3374
3375     int graphic;
3376     int sync_frame;
3377     int frame;
3378
3379     if (!IS_MOVING(jx, jy))             /* push movement already finished */
3380     {
3381       element = Feld[next_jx][next_jy];
3382       gfx_frame = GfxFrame[next_jx][next_jy];
3383     }
3384
3385     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3386
3387     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3388     frame = getGraphicAnimationFrame(graphic, sync_frame);
3389
3390     /* draw background element under pushed element (like the Sokoban field) */
3391     if (game.use_masked_pushing && IS_MOVING(jx, jy))
3392     {
3393       /* this allows transparent pushing animation over non-black background */
3394
3395       if (Back[jx][jy])
3396         DrawLevelElement(jx, jy, Back[jx][jy]);
3397       else
3398         DrawLevelElement(jx, jy, EL_EMPTY);
3399
3400       if (Back[next_jx][next_jy])
3401         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3402       else
3403         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3404     }
3405     else if (Back[next_jx][next_jy])
3406       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3407
3408 #if 1
3409     /* do not draw (EM style) pushing animation when pushing is finished */
3410     /* (two-tile animations usually do not contain start and end frame) */
3411     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3412       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3413     else
3414       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3415 #else
3416     /* masked drawing is needed for EMC style (double) movement graphics */
3417     /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3418     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3419 #endif
3420   }
3421
3422 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3423   /* ----------------------------------------------------------------------- */
3424   /* draw player himself                                                     */
3425   /* ----------------------------------------------------------------------- */
3426
3427   graphic = getPlayerGraphic(player, move_dir);
3428
3429   /* in the case of changed player action or direction, prevent the current
3430      animation frame from being restarted for identical animations */
3431   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3432     player->Frame = last_player_frame;
3433
3434   frame = getGraphicAnimationFrame(graphic, player->Frame);
3435
3436   if (player->GfxPos)
3437   {
3438     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3439       sxx = player->GfxPos;
3440     else
3441       syy = player->GfxPos;
3442   }
3443
3444   if (player_is_opaque)
3445     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3446   else
3447     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3448
3449   if (SHIELD_ON(player))
3450   {
3451     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3452                    IMG_SHIELD_NORMAL_ACTIVE);
3453     int frame = getGraphicAnimationFrame(graphic, -1);
3454
3455     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3456   }
3457 #endif
3458
3459   /* ----------------------------------------------------------------------- */
3460   /* draw things in front of player (active dynamite or dynabombs)           */
3461   /* ----------------------------------------------------------------------- */
3462
3463   if (IS_ACTIVE_BOMB(element))
3464   {
3465     graphic = el2img(element);
3466     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3467
3468     if (game.emulation == EMU_SUPAPLEX)
3469       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3470     else
3471       DrawGraphicThruMask(sx, sy, graphic, frame);
3472   }
3473
3474   if (player_is_moving && last_element == EL_EXPLOSION)
3475   {
3476     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3477                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
3478     int graphic = el_act2img(element, ACTION_EXPLODING);
3479     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3480     int phase = ExplodePhase[last_jx][last_jy] - 1;
3481     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3482
3483     if (phase >= delay)
3484       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3485   }
3486
3487   /* ----------------------------------------------------------------------- */
3488   /* draw elements the player is just walking/passing through/under          */
3489   /* ----------------------------------------------------------------------- */
3490
3491   if (player_is_moving)
3492   {
3493     /* handle the field the player is leaving ... */
3494     if (IS_ACCESSIBLE_INSIDE(last_element))
3495       DrawLevelField(last_jx, last_jy);
3496     else if (IS_ACCESSIBLE_UNDER(last_element))
3497       DrawLevelFieldThruMask(last_jx, last_jy);
3498   }
3499
3500   /* do not redraw accessible elements if the player is just pushing them */
3501   if (!player_is_moving || !player->is_pushing)
3502   {
3503     /* ... and the field the player is entering */
3504     if (IS_ACCESSIBLE_INSIDE(element))
3505       DrawLevelField(jx, jy);
3506     else if (IS_ACCESSIBLE_UNDER(element))
3507       DrawLevelFieldThruMask(jx, jy);
3508   }
3509
3510   MarkTileDirty(sx, sy);
3511 }
3512
3513 /* ------------------------------------------------------------------------- */
3514
3515 void WaitForEventToContinue()
3516 {
3517   boolean still_wait = TRUE;
3518
3519   /* simulate releasing mouse button over last gadget, if still pressed */
3520   if (button_status)
3521     HandleGadgets(-1, -1, 0);
3522
3523   button_status = MB_RELEASED;
3524
3525   ClearEventQueue();
3526
3527   while (still_wait)
3528   {
3529     if (PendingEvent())
3530     {
3531       Event event;
3532
3533       NextEvent(&event);
3534
3535       switch (event.type)
3536       {
3537         case EVENT_BUTTONPRESS:
3538         case EVENT_KEYPRESS:
3539           still_wait = FALSE;
3540           break;
3541
3542         case EVENT_KEYRELEASE:
3543           ClearPlayerAction();
3544           break;
3545
3546         default:
3547           HandleOtherEvents(&event);
3548           break;
3549       }
3550     }
3551     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3552     {
3553       still_wait = FALSE;
3554     }
3555
3556     DoAnimation();
3557
3558     BackToFront();
3559   }
3560 }
3561
3562 #define MAX_REQUEST_LINES               13
3563 #define MAX_REQUEST_LINE_FONT1_LEN      7
3564 #define MAX_REQUEST_LINE_FONT2_LEN      10
3565
3566 static int RequestHandleEvents(unsigned int req_state)
3567 {
3568   boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3569                           local_player->LevelSolved_GameEnd);
3570   int width  = request.width;
3571   int height = request.height;
3572   int sx, sy;
3573   int result;
3574
3575   setRequestPosition(&sx, &sy, FALSE);
3576
3577   button_status = MB_RELEASED;
3578
3579   request_gadget_id = -1;
3580   result = -1;
3581
3582   while (result < 0)
3583   {
3584     if (level_solved)
3585     {
3586       SetDrawtoField(DRAW_FIELDBUFFER);
3587
3588       HandleGameActions();
3589
3590       SetDrawtoField(DRAW_BACKBUFFER);
3591
3592       if (global.use_envelope_request)
3593       {
3594         /* copy current state of request area to middle of playfield area */
3595         BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3596       }
3597     }
3598
3599     if (PendingEvent())
3600     {
3601       Event event;
3602
3603       while (NextValidEvent(&event))
3604       {
3605         switch (event.type)
3606         {
3607           case EVENT_BUTTONPRESS:
3608           case EVENT_BUTTONRELEASE:
3609           case EVENT_MOTIONNOTIFY:
3610           {
3611             int mx, my;
3612
3613             if (event.type == EVENT_MOTIONNOTIFY)
3614             {
3615               if (!button_status)
3616                 continue;
3617
3618               motion_status = TRUE;
3619               mx = ((MotionEvent *) &event)->x;
3620               my = ((MotionEvent *) &event)->y;
3621             }
3622             else
3623             {
3624               motion_status = FALSE;
3625               mx = ((ButtonEvent *) &event)->x;
3626               my = ((ButtonEvent *) &event)->y;
3627               if (event.type == EVENT_BUTTONPRESS)
3628                 button_status = ((ButtonEvent *) &event)->button;
3629               else
3630                 button_status = MB_RELEASED;
3631             }
3632
3633             /* this sets 'request_gadget_id' */
3634             HandleGadgets(mx, my, button_status);
3635
3636             switch (request_gadget_id)
3637             {
3638               case TOOL_CTRL_ID_YES:
3639                 result = TRUE;
3640                 break;
3641               case TOOL_CTRL_ID_NO:
3642                 result = FALSE;
3643                 break;
3644               case TOOL_CTRL_ID_CONFIRM:
3645                 result = TRUE | FALSE;
3646                 break;
3647
3648               case TOOL_CTRL_ID_PLAYER_1:
3649                 result = 1;
3650                 break;
3651               case TOOL_CTRL_ID_PLAYER_2:
3652                 result = 2;
3653                 break;
3654               case TOOL_CTRL_ID_PLAYER_3:
3655                 result = 3;
3656                 break;
3657               case TOOL_CTRL_ID_PLAYER_4:
3658                 result = 4;
3659                 break;
3660
3661               default:
3662                 break;
3663             }
3664
3665             break;
3666           }
3667
3668           case EVENT_KEYPRESS:
3669             switch (GetEventKey((KeyEvent *)&event, TRUE))
3670             {
3671               case KSYM_space:
3672                 if (req_state & REQ_CONFIRM)
3673                   result = 1;
3674                 break;
3675
3676               case KSYM_Return:
3677 #if defined(TARGET_SDL2)
3678               case KSYM_Menu:
3679 #endif
3680                 result = 1;
3681                 break;
3682
3683               case KSYM_Escape:
3684 #if defined(TARGET_SDL2)
3685               case KSYM_Back:
3686 #endif
3687                 result = 0;
3688                 break;
3689
3690               default:
3691                 break;
3692             }
3693
3694             if (req_state & REQ_PLAYER)
3695               result = 0;
3696             break;
3697
3698           case EVENT_KEYRELEASE:
3699             ClearPlayerAction();
3700             break;
3701
3702           default:
3703             HandleOtherEvents(&event);
3704             break;
3705         }
3706       }
3707     }
3708     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3709     {
3710       int joy = AnyJoystick();
3711
3712       if (joy & JOY_BUTTON_1)
3713         result = 1;
3714       else if (joy & JOY_BUTTON_2)
3715         result = 0;
3716     }
3717
3718     if (level_solved)
3719     {
3720       if (global.use_envelope_request)
3721       {
3722         /* copy back current state of pressed buttons inside request area */
3723         BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3724       }
3725     }
3726     else
3727     {
3728       DoAnimation();
3729     }
3730
3731     BackToFront();
3732   }
3733
3734   return result;
3735 }
3736
3737 static boolean RequestDoor(char *text, unsigned int req_state)
3738 {
3739   unsigned int old_door_state;
3740   int 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   SetFontStatus(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   ResetFontStatus();
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_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
4047     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_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_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
4694     TOOL_CTRL_ID_YES,                   "yes"
4695   },
4696   {
4697     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
4698     TOOL_CTRL_ID_NO,                    "no"
4699   },
4700   {
4701     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
4702     TOOL_CTRL_ID_CONFIRM,               "confirm"
4703   },
4704   {
4705     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
4706     TOOL_CTRL_ID_PLAYER_1,              "player 1"
4707   },
4708   {
4709     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
4710     TOOL_CTRL_ID_PLAYER_2,              "player 2"
4711   },
4712   {
4713     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
4714     TOOL_CTRL_ID_PLAYER_3,              "player 3"
4715   },
4716   {
4717     IMG_GFX_REQUEST_BUTTON_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_window_scaling_percent = (!video.fullscreen_enabled &&
8214                                            setup.window_scaling_percent !=
8215                                            video.window_scaling_percent);
8216
8217   if (change_window_scaling_percent && video.fullscreen_enabled)
8218     return;
8219
8220   if (!change_window_scaling_percent && !video.fullscreen_available)
8221     return;
8222
8223 #if defined(TARGET_SDL2)
8224   if (change_window_scaling_percent)
8225   {
8226     SDLSetWindowScaling(setup.window_scaling_percent);
8227
8228     return;
8229   }
8230   else if (change_fullscreen)
8231   {
8232     SDLSetWindowFullscreen(setup.fullscreen);
8233
8234     /* set setup value according to successfully changed fullscreen mode */
8235     setup.fullscreen = video.fullscreen_enabled;
8236
8237     return;
8238   }
8239 #endif
8240
8241   if (change_fullscreen ||
8242       change_window_scaling_percent)
8243   {
8244     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8245
8246     /* save backbuffer content which gets lost when toggling fullscreen mode */
8247     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8248
8249     if (change_window_scaling_percent)
8250     {
8251       /* keep window mode, but change window scaling */
8252       video.fullscreen_enabled = TRUE;          /* force new window scaling */
8253     }
8254
8255     /* toggle fullscreen */
8256     ChangeVideoModeIfNeeded(setup.fullscreen);
8257
8258     /* set setup value according to successfully changed fullscreen mode */
8259     setup.fullscreen = video.fullscreen_enabled;
8260
8261     /* restore backbuffer content from temporary backbuffer backup bitmap */
8262     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8263
8264     FreeBitmap(tmp_backbuffer);
8265
8266     /* update visible window/screen */
8267     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8268   }
8269 }
8270
8271 void JoinRectangles(int *x, int *y, int *width, int *height,
8272                     int x2, int y2, int width2, int height2)
8273 {
8274   // do not join with "off-screen" rectangle
8275   if (x2 == -1 || y2 == -1)
8276     return;
8277
8278   *x = MIN(*x, x2);
8279   *y = MIN(*y, y2);
8280   *width = MAX(*width, width2);
8281   *height = MAX(*height, height2);
8282 }
8283
8284 void SetAnimStatus(int anim_status_new)
8285 {
8286   if (anim_status_new == GAME_MODE_MAIN)
8287     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8288
8289   global.anim_status_next = anim_status_new;
8290
8291   // directly set screen modes that are entered without fading
8292   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
8293        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8294       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
8295        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8296     global.anim_status = global.anim_status_next;
8297 }
8298
8299 void SetGameStatus(int game_status_new)
8300 {
8301   game_status = game_status_new;
8302
8303   SetAnimStatus(game_status_new);
8304 }
8305
8306 void SetFontStatus(int game_status_new)
8307 {
8308   static int last_game_status = -1;
8309
8310   if (game_status_new != -1)
8311   {
8312     // set game status for font use after storing last game status
8313     last_game_status = game_status;
8314     game_status = game_status_new;
8315   }
8316   else
8317   {
8318     // reset game status after font use from last stored game status
8319     game_status = last_game_status;
8320   }
8321 }
8322
8323 void ResetFontStatus()
8324 {
8325   SetFontStatus(-1);
8326 }
8327
8328 void ChangeViewportPropertiesIfNeeded()
8329 {
8330   int gfx_game_mode = game_status;
8331   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8332                         game_status);
8333   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
8334   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8335   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
8336   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
8337   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
8338   int new_win_xsize     = vp_window->width;
8339   int new_win_ysize     = vp_window->height;
8340   int border_size       = vp_playfield->border_size;
8341   int new_sx            = vp_playfield->x + border_size;
8342   int new_sy            = vp_playfield->y + border_size;
8343   int new_sxsize        = vp_playfield->width  - 2 * border_size;
8344   int new_sysize        = vp_playfield->height - 2 * border_size;
8345   int new_real_sx       = vp_playfield->x;
8346   int new_real_sy       = vp_playfield->y;
8347   int new_full_sxsize   = vp_playfield->width;
8348   int new_full_sysize   = vp_playfield->height;
8349   int new_dx            = vp_door_1->x;
8350   int new_dy            = vp_door_1->y;
8351   int new_dxsize        = vp_door_1->width;
8352   int new_dysize        = vp_door_1->height;
8353   int new_vx            = vp_door_2->x;
8354   int new_vy            = vp_door_2->y;
8355   int new_vxsize        = vp_door_2->width;
8356   int new_vysize        = vp_door_2->height;
8357   int new_ex            = vp_door_3->x;
8358   int new_ey            = vp_door_3->y;
8359   int new_exsize        = vp_door_3->width;
8360   int new_eysize        = vp_door_3->height;
8361   int new_tilesize_var =
8362     (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8363
8364   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8365                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8366   int new_scr_fieldx = new_sxsize / tilesize;
8367   int new_scr_fieldy = new_sysize / tilesize;
8368   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8369   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8370   boolean init_gfx_buffers = FALSE;
8371   boolean init_video_buffer = FALSE;
8372   boolean init_gadgets_and_toons = FALSE;
8373   boolean init_em_graphics = FALSE;
8374
8375   if (new_win_xsize != WIN_XSIZE ||
8376       new_win_ysize != WIN_YSIZE)
8377   {
8378     WIN_XSIZE = new_win_xsize;
8379     WIN_YSIZE = new_win_ysize;
8380
8381     init_video_buffer = TRUE;
8382     init_gfx_buffers = TRUE;
8383     init_gadgets_and_toons = TRUE;
8384
8385     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8386   }
8387
8388   if (new_scr_fieldx != SCR_FIELDX ||
8389       new_scr_fieldy != SCR_FIELDY)
8390   {
8391     /* this always toggles between MAIN and GAME when using small tile size */
8392
8393     SCR_FIELDX = new_scr_fieldx;
8394     SCR_FIELDY = new_scr_fieldy;
8395
8396     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8397   }
8398
8399   if (new_sx != SX ||
8400       new_sy != SY ||
8401       new_dx != DX ||
8402       new_dy != DY ||
8403       new_vx != VX ||
8404       new_vy != VY ||
8405       new_ex != EX ||
8406       new_ey != EY ||
8407       new_sxsize != SXSIZE ||
8408       new_sysize != SYSIZE ||
8409       new_dxsize != DXSIZE ||
8410       new_dysize != DYSIZE ||
8411       new_vxsize != VXSIZE ||
8412       new_vysize != VYSIZE ||
8413       new_exsize != EXSIZE ||
8414       new_eysize != EYSIZE ||
8415       new_real_sx != REAL_SX ||
8416       new_real_sy != REAL_SY ||
8417       new_full_sxsize != FULL_SXSIZE ||
8418       new_full_sysize != FULL_SYSIZE ||
8419       new_tilesize_var != TILESIZE_VAR
8420       )
8421   {
8422     // ------------------------------------------------------------------------
8423     // determine next fading area for changed viewport definitions
8424     // ------------------------------------------------------------------------
8425
8426     // start with current playfield area (default fading area)
8427     FADE_SX = REAL_SX;
8428     FADE_SY = REAL_SY;
8429     FADE_SXSIZE = FULL_SXSIZE;
8430     FADE_SYSIZE = FULL_SYSIZE;
8431
8432     // add new playfield area if position or size has changed
8433     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8434         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8435     {
8436       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8437                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8438     }
8439
8440     // add current and new door 1 area if position or size has changed
8441     if (new_dx != DX || new_dy != DY ||
8442         new_dxsize != DXSIZE || new_dysize != DYSIZE)
8443     {
8444       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8445                      DX, DY, DXSIZE, DYSIZE);
8446       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8447                      new_dx, new_dy, new_dxsize, new_dysize);
8448     }
8449
8450     // add current and new door 2 area if position or size has changed
8451     if (new_dx != VX || new_dy != VY ||
8452         new_dxsize != VXSIZE || new_dysize != VYSIZE)
8453     {
8454       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8455                      VX, VY, VXSIZE, VYSIZE);
8456       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8457                      new_vx, new_vy, new_vxsize, new_vysize);
8458     }
8459
8460     // ------------------------------------------------------------------------
8461     // handle changed tile size
8462     // ------------------------------------------------------------------------
8463
8464     if (new_tilesize_var != TILESIZE_VAR)
8465     {
8466       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8467
8468       // changing tile size invalidates scroll values of engine snapshots
8469       FreeEngineSnapshotSingle();
8470
8471       // changing tile size requires update of graphic mapping for EM engine
8472       init_em_graphics = TRUE;
8473     }
8474
8475     SX = new_sx;
8476     SY = new_sy;
8477     DX = new_dx;
8478     DY = new_dy;
8479     VX = new_vx;
8480     VY = new_vy;
8481     EX = new_ex;
8482     EY = new_ey;
8483     SXSIZE = new_sxsize;
8484     SYSIZE = new_sysize;
8485     DXSIZE = new_dxsize;
8486     DYSIZE = new_dysize;
8487     VXSIZE = new_vxsize;
8488     VYSIZE = new_vysize;
8489     EXSIZE = new_exsize;
8490     EYSIZE = new_eysize;
8491     REAL_SX = new_real_sx;
8492     REAL_SY = new_real_sy;
8493     FULL_SXSIZE = new_full_sxsize;
8494     FULL_SYSIZE = new_full_sysize;
8495     TILESIZE_VAR = new_tilesize_var;
8496
8497     init_gfx_buffers = TRUE;
8498     init_gadgets_and_toons = TRUE;
8499
8500     // printf("::: viewports: init_gfx_buffers\n");
8501     // printf("::: viewports: init_gadgets_and_toons\n");
8502   }
8503
8504   if (init_gfx_buffers)
8505   {
8506     // printf("::: init_gfx_buffers\n");
8507
8508     SCR_FIELDX = new_scr_fieldx_buffers;
8509     SCR_FIELDY = new_scr_fieldy_buffers;
8510
8511     InitGfxBuffers();
8512
8513     SCR_FIELDX = new_scr_fieldx;
8514     SCR_FIELDY = new_scr_fieldy;
8515
8516     SetDrawDeactivationMask(REDRAW_NONE);
8517     SetDrawBackgroundMask(REDRAW_FIELD);
8518   }
8519
8520   if (init_video_buffer)
8521   {
8522     // printf("::: init_video_buffer\n");
8523
8524     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8525     InitImageTextures();
8526   }
8527
8528   if (init_gadgets_and_toons)
8529   {
8530     // printf("::: init_gadgets_and_toons\n");
8531
8532     InitGadgets();
8533     InitToons();
8534     InitGlobalAnimations();
8535   }
8536
8537   if (init_em_graphics)
8538   {
8539       InitGraphicInfo_EM();
8540   }
8541 }