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