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