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