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