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