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