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