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