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