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