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