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