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