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