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