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